Compare commits
38 Commits
temp-multi
...
temp-geome
Author | SHA1 | Date | |
---|---|---|---|
fb0d5124f2 | |||
6146a679c9 | |||
65a1ec89ba | |||
ef9fbf258b | |||
a448949f25 | |||
e3232f987a | |||
7ebc3140bb | |||
be1891e895 | |||
cbe4036406 | |||
eb0c50ac78 | |||
6e51ef9531 | |||
c42ceef040 | |||
![]() |
ebdae75736 | ||
7d17f2addf | |||
4c6d207343 | |||
42f89b9212 | |||
153b45037f | |||
daa7c59e38 | |||
344aca3b1b | |||
064167fce7 | |||
a95e56b741 | |||
8f4730e66f | |||
![]() |
21d4a888b8 | ||
acc2e8afa9 | |||
04bb1bda32 | |||
![]() |
cd118c5581 | ||
27f138f4c8 | |||
2f0e350ffd | |||
b4b3f518aa | |||
5aa3167e48 | |||
8165333de9 | |||
9564b6cf23 | |||
0682af0d63 | |||
62f2204d65 | |||
aa067bef5e | |||
0de3d4e8c7 | |||
b477333473 | |||
a1e91fbef3 |
@@ -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)
|
||||
|
@@ -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
source/blender/blenkernel/BKE_anonymous_attribute.h
Normal file
37
source/blender/blenkernel/BKE_anonymous_attribute.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct AnonymousAttributeID AnonymousAttributeID;
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name);
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name);
|
||||
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id);
|
||||
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id);
|
||||
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
223
source/blender/blenkernel/BKE_anonymous_attribute.hh
Normal file
223
source/blender/blenkernel/BKE_anonymous_attribute.hh
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<bool IsStrongReference> class OwnedAnonymousAttributeID {
|
||||
private:
|
||||
const AnonymousAttributeID *data_ = nullptr;
|
||||
|
||||
public:
|
||||
OwnedAnonymousAttributeID() = default;
|
||||
|
||||
explicit OwnedAnonymousAttributeID(StringRefNull debug_name)
|
||||
{
|
||||
if constexpr (IsStrongReference) {
|
||||
data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str());
|
||||
}
|
||||
else {
|
||||
data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/* This transfers ownership, so no incref is necessary. */
|
||||
explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id)
|
||||
: data_(anonymous_id)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
this->incref();
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
this->incref();
|
||||
other.decref();
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
|
||||
~OwnedAnonymousAttributeID()
|
||||
{
|
||||
this->decref();
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~OwnedAnonymousAttributeID();
|
||||
new (this) OwnedAnonymousAttributeID(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~OwnedAnonymousAttributeID();
|
||||
new (this) OwnedAnonymousAttributeID(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return data_ != nullptr;
|
||||
}
|
||||
|
||||
StringRefNull debug_name() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return BKE_anonymous_attribute_id_debug_name(data_);
|
||||
}
|
||||
|
||||
bool has_strong_references() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return BKE_anonymous_attribute_id_has_strong_references(data_);
|
||||
}
|
||||
|
||||
const AnonymousAttributeID *extract()
|
||||
{
|
||||
const AnonymousAttributeID *extracted_data = data_;
|
||||
/* Don't decref because the caller becomes the new owner. */
|
||||
data_ = nullptr;
|
||||
return extracted_data;
|
||||
}
|
||||
|
||||
const AnonymousAttributeID *get()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
void incref()
|
||||
{
|
||||
if (data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if constexpr (IsStrongReference) {
|
||||
BKE_anonymous_attribute_id_increment_strong(data_);
|
||||
}
|
||||
else {
|
||||
BKE_anonymous_attribute_id_increment_weak(data_);
|
||||
}
|
||||
}
|
||||
|
||||
void decref()
|
||||
{
|
||||
if (data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if constexpr (IsStrongReference) {
|
||||
BKE_anonymous_attribute_id_decrement_strong(data_);
|
||||
}
|
||||
else {
|
||||
BKE_anonymous_attribute_id_decrement_weak(data_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using StrongAnonymousAttributeID = OwnedAnonymousAttributeID<true>;
|
||||
using WeakAnonymousAttributeID = OwnedAnonymousAttributeID<false>;
|
||||
|
||||
class AttributeIDRef {
|
||||
private:
|
||||
StringRef name_;
|
||||
const AnonymousAttributeID *anonymous_id_ = nullptr;
|
||||
|
||||
public:
|
||||
AttributeIDRef() = default;
|
||||
|
||||
AttributeIDRef(StringRef name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
AttributeIDRef(StringRefNull name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
AttributeIDRef(const char *name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
AttributeIDRef(const std::string &name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
|
||||
AttributeIDRef(const AnonymousAttributeID *anonymous_id) : anonymous_id_(anonymous_id)
|
||||
{
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->is_named() || this->is_anonymous();
|
||||
}
|
||||
|
||||
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
|
||||
{
|
||||
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return get_default_hash_2(name_, anonymous_id_);
|
||||
}
|
||||
|
||||
bool is_named() const
|
||||
{
|
||||
return !name_.is_empty();
|
||||
}
|
||||
|
||||
bool is_anonymous() const
|
||||
{
|
||||
return anonymous_id_ != nullptr;
|
||||
}
|
||||
|
||||
StringRef name() const
|
||||
{
|
||||
BLI_assert(this->is_named());
|
||||
return name_;
|
||||
}
|
||||
|
||||
const AnonymousAttributeID &anonymous_id() const
|
||||
{
|
||||
BLI_assert(this->is_anonymous());
|
||||
return *anonymous_id_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
@@ -22,6 +22,7 @@
|
||||
#include "FN_generic_span.hh"
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
#include "BLI_color.hh"
|
||||
@@ -104,8 +105,8 @@ struct AttributeInitMove : public AttributeInit {
|
||||
};
|
||||
|
||||
/* Returns false when the iteration should be stopped. */
|
||||
using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
|
||||
const AttributeMetaData &meta_data)>;
|
||||
using AttributeForeachCallback = blender::FunctionRef<bool(
|
||||
const blender::bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
@@ -333,26 +334,30 @@ class CustomDataAttributes {
|
||||
|
||||
void reallocate(const int size);
|
||||
|
||||
std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const;
|
||||
std::optional<blender::fn::GSpan> get_for_read(
|
||||
const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
blender::fn::GVArrayPtr get_for_read(const StringRef name,
|
||||
blender::fn::GVArrayPtr get_for_read(const AttributeIDRef &attribute_id,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const;
|
||||
|
||||
template<typename T>
|
||||
blender::fn::GVArray_Typed<T> get_for_read(const blender::StringRef name,
|
||||
blender::fn::GVArray_Typed<T> get_for_read(const blender::bke::AttributeIDRef &attribute_id,
|
||||
const T &default_value) const
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
GVArrayPtr varray = this->get_for_read(name, type, &default_value);
|
||||
GVArrayPtr varray = this->get_for_read(attribute_id, type, &default_value);
|
||||
return blender::fn::GVArray_Typed<T>(std::move(varray));
|
||||
}
|
||||
|
||||
std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name);
|
||||
bool create(const blender::StringRef name, const CustomDataType data_type);
|
||||
bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
|
||||
bool remove(const blender::StringRef name);
|
||||
std::optional<blender::fn::GMutableSpan> get_for_write(
|
||||
const blender::bke::AttributeIDRef &attribute_id);
|
||||
bool create(const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type);
|
||||
bool create_by_move(const blender::bke::AttributeIDRef &attribute_id,
|
||||
const CustomDataType data_type,
|
||||
void *buffer);
|
||||
bool remove(const blender::bke::AttributeIDRef &attribute_id);
|
||||
|
||||
bool foreach_attribute(const AttributeForeachCallback callback,
|
||||
const AttributeDomain domain) const;
|
||||
|
@@ -33,6 +33,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct AnonymousAttributeID;
|
||||
struct BMesh;
|
||||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
@@ -193,6 +194,12 @@ void *CustomData_add_layer_named(struct CustomData *data,
|
||||
void *layer,
|
||||
int totelem,
|
||||
const char *name);
|
||||
void *CustomData_add_layer_anonymous(struct CustomData *data,
|
||||
int type,
|
||||
eCDAllocType alloctype,
|
||||
void *layer,
|
||||
int totelem,
|
||||
const struct AnonymousAttributeID *anonymous_id);
|
||||
|
||||
/* frees the active or first data layer with the give type.
|
||||
* returns 1 on success, 0 if no layer with the given type is found
|
||||
@@ -231,6 +238,11 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data,
|
||||
const int type,
|
||||
const char *name,
|
||||
const int totelem);
|
||||
void *CustomData_duplicate_referenced_layer_anonymous(
|
||||
CustomData *data,
|
||||
const int type,
|
||||
const struct AnonymousAttributeID *anonymous_id,
|
||||
const int totelem);
|
||||
bool CustomData_is_referenced_layer(struct CustomData *data, int type);
|
||||
|
||||
/* Duplicate all the layers with flag NOFREE, and remove the flag from duplicated layers. */
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include "BLI_user_counter.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_geometry_set.h"
|
||||
|
||||
@@ -88,11 +89,11 @@ class GeometryComponent {
|
||||
GeometryComponentType type() const;
|
||||
|
||||
/* Return true when any attribute with this name exists, including built in attributes. */
|
||||
bool attribute_exists(const blender::StringRef attribute_name) const;
|
||||
bool attribute_exists(const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
/* Return the data type and domain of an attribute with the given name if it exists. */
|
||||
std::optional<AttributeMetaData> attribute_get_meta_data(
|
||||
const blender::StringRef attribute_name) const;
|
||||
const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
/* Returns true when the geometry component supports this attribute domain. */
|
||||
bool attribute_domain_supported(const AttributeDomain domain) const;
|
||||
@@ -104,12 +105,12 @@ class GeometryComponent {
|
||||
/* Get read-only access to the highest priority attribute with the given name.
|
||||
* Returns null if the attribute does not exist. */
|
||||
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name) const;
|
||||
const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
/* Get read and write access to the highest priority attribute with the given name.
|
||||
* Returns null if the attribute does not exist. */
|
||||
blender::bke::WriteAttributeLookup attribute_try_get_for_write(
|
||||
const blender::StringRef attribute_name);
|
||||
const blender::bke::AttributeIDRef &attribute_id);
|
||||
|
||||
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
|
||||
* interpolate from one domain to another.
|
||||
@@ -120,10 +121,10 @@ class GeometryComponent {
|
||||
const AttributeDomain to_domain) const;
|
||||
|
||||
/* Returns true when the attribute has been deleted. */
|
||||
bool attribute_try_delete(const blender::StringRef attribute_name);
|
||||
bool attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id);
|
||||
|
||||
/* Returns true when the attribute has been created. */
|
||||
bool attribute_try_create(const blender::StringRef attribute_name,
|
||||
bool attribute_try_create(const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer);
|
||||
@@ -133,7 +134,7 @@ class GeometryComponent {
|
||||
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
|
||||
const AttributeInit &initializer);
|
||||
|
||||
blender::Set<std::string> attribute_names() const;
|
||||
blender::Set<blender::bke::AttributeIDRef> attribute_ids() const;
|
||||
bool attribute_foreach(const AttributeForeachCallback callback) const;
|
||||
|
||||
virtual bool is_empty() const;
|
||||
@@ -142,7 +143,7 @@ class GeometryComponent {
|
||||
* 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 blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const;
|
||||
|
||||
@@ -150,18 +151,18 @@ class GeometryComponent {
|
||||
* left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
|
||||
* requested domain. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name, const AttributeDomain domain) const;
|
||||
const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) const;
|
||||
|
||||
/* Get a virtual array to read data of an attribute with the given data type. The domain is
|
||||
* left unchanged. Returns null when the attribute does not exist or cannot be converted to the
|
||||
* requested data type. */
|
||||
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name, const CustomDataType data_type) const;
|
||||
const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type) const;
|
||||
|
||||
/* Get a virtual array to read the data of an attribute. If that is not possible, the returned
|
||||
* virtual array will contain a default value. This never returns null. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
|
||||
const blender::StringRef attribute_name,
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value = nullptr) const;
|
||||
@@ -169,14 +170,15 @@ class GeometryComponent {
|
||||
/* Should be used instead of the method above when the requested data type is known at compile
|
||||
* time for better type safety. */
|
||||
template<typename T>
|
||||
blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value) const
|
||||
blender::fn::GVArray_Typed<T> attribute_get_for_read(
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value) const
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
std::unique_ptr varray = this->attribute_get_for_read(
|
||||
attribute_name, domain, type, &default_value);
|
||||
attribute_id, domain, type, &default_value);
|
||||
return blender::fn::GVArray_Typed<T>(std::move(varray));
|
||||
}
|
||||
|
||||
@@ -191,7 +193,7 @@ class GeometryComponent {
|
||||
* is created that will overwrite the existing attribute in the end.
|
||||
*/
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output(
|
||||
const blender::StringRef attribute_name,
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value = nullptr);
|
||||
@@ -200,28 +202,30 @@ class GeometryComponent {
|
||||
* 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 blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type);
|
||||
|
||||
/* Statically typed method corresponding to the equally named generic one. */
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
|
||||
const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value)
|
||||
const blender::bke::AttributeIDRef &attribute_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_for_output(attribute_name, domain, data_type, &default_value);
|
||||
return this->attribute_try_get_for_output(attribute_id, domain, data_type, &default_value);
|
||||
}
|
||||
|
||||
/* Statically typed method corresponding to the equally named generic one. */
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
|
||||
const blender::StringRef attribute_name, const AttributeDomain domain)
|
||||
const blender::bke::AttributeIDRef &attribute_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_for_output_only(attribute_name, domain, data_type);
|
||||
return this->attribute_try_get_for_output_only(attribute_id, domain, data_type);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@@ -59,9 +59,10 @@ struct AttributeKind {
|
||||
* will contain the highest complexity data type and the highest priority domain among every
|
||||
* attribute with the given name on all of the input components.
|
||||
*/
|
||||
void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
|
||||
Span<GeometryComponentType> component_types,
|
||||
const Set<std::string> &ignored_attributes,
|
||||
Map<std::string, AttributeKind> &r_attributes);
|
||||
void geometry_set_gather_instances_attribute_info(
|
||||
Span<GeometryInstanceGroup> set_groups,
|
||||
Span<GeometryComponentType> component_types,
|
||||
const Set<std::string> &ignored_attributes,
|
||||
Map<AttributeIDRef, AttributeKind> &r_attributes);
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@@ -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 */
|
||||
|
@@ -76,6 +76,7 @@ set(SRC
|
||||
intern/anim_path.c
|
||||
intern/anim_sys.c
|
||||
intern/anim_visualization.c
|
||||
intern/anonymous_attribute.cc
|
||||
intern/appdir.c
|
||||
intern/armature.c
|
||||
intern/armature_selection.cc
|
||||
@@ -295,6 +296,8 @@ set(SRC
|
||||
BKE_anim_path.h
|
||||
BKE_anim_visualization.h
|
||||
BKE_animsys.h
|
||||
BKE_anonymous_attribute.h
|
||||
BKE_anonymous_attribute.hh
|
||||
BKE_appdir.h
|
||||
BKE_armature.h
|
||||
BKE_armature.hh
|
||||
|
92
source/blender/blenkernel/intern/anonymous_attribute.cc
Normal file
92
source/blender/blenkernel/intern/anonymous_attribute.cc
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
|
||||
using namespace blender::bke;
|
||||
|
||||
struct AnonymousAttributeID {
|
||||
mutable std::atomic<int> refcount_weak = 0;
|
||||
mutable std::atomic<int> refcount_strong = 0;
|
||||
std::string debug_name;
|
||||
std::string internal_name;
|
||||
};
|
||||
|
||||
static std::string get_new_internal_name()
|
||||
{
|
||||
static std::atomic<int> index = 0;
|
||||
const int next_index = index.fetch_add(1);
|
||||
return "anonymous_attribute_" + std::to_string(next_index);
|
||||
}
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name)
|
||||
{
|
||||
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
|
||||
anonymous_id->debug_name = debug_name;
|
||||
anonymous_id->internal_name = get_new_internal_name();
|
||||
anonymous_id->refcount_weak.store(1);
|
||||
return anonymous_id;
|
||||
}
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name)
|
||||
{
|
||||
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
|
||||
anonymous_id->debug_name = debug_name;
|
||||
anonymous_id->internal_name = get_new_internal_name();
|
||||
anonymous_id->refcount_weak.store(1);
|
||||
anonymous_id->refcount_strong.store(1);
|
||||
return anonymous_id;
|
||||
}
|
||||
|
||||
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->refcount_strong.load() >= 1;
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_weak.fetch_add(1);
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_weak.fetch_add(1);
|
||||
anonymous_id->refcount_strong.fetch_add(1);
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
const int new_refcount = anonymous_id->refcount_weak.fetch_sub(1) - 1;
|
||||
if (new_refcount == 0) {
|
||||
delete anonymous_id;
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_strong.fetch_sub(1);
|
||||
BKE_anonymous_attribute_id_decrement_weak(anonymous_id);
|
||||
}
|
||||
|
||||
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->debug_name.c_str();
|
||||
}
|
||||
|
||||
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->internal_name.c_str();
|
||||
}
|
@@ -334,8 +334,20 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component)
|
||||
return data != nullptr;
|
||||
}
|
||||
|
||||
static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
|
||||
const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (!attribute_id) {
|
||||
return false;
|
||||
}
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return layer.anonymous_id == &attribute_id.anonymous_id();
|
||||
}
|
||||
return layer.name == attribute_id.name();
|
||||
}
|
||||
|
||||
ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
|
||||
const GeometryComponent &component, const StringRef attribute_name) const
|
||||
const GeometryComponent &component, const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
@@ -343,7 +355,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
|
||||
}
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
||||
if (layer.name != attribute_name) {
|
||||
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
continue;
|
||||
}
|
||||
const CustomDataType data_type = (CustomDataType)layer.type;
|
||||
@@ -368,7 +380,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
|
||||
}
|
||||
|
||||
WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
|
||||
GeometryComponent &component, const StringRef attribute_name) const
|
||||
GeometryComponent &component, const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
@@ -376,10 +388,17 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
|
||||
}
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
|
||||
if (layer.name != attribute_name) {
|
||||
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
continue;
|
||||
}
|
||||
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
|
||||
if (attribute_id.is_named()) {
|
||||
CustomData_duplicate_referenced_layer_named(
|
||||
custom_data, layer.type, layer.name, domain_size);
|
||||
}
|
||||
else {
|
||||
CustomData_duplicate_referenced_layer_anonymous(
|
||||
custom_data, layer.type, &attribute_id.anonymous_id(), domain_size);
|
||||
}
|
||||
const CustomDataType data_type = (CustomDataType)layer.type;
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT:
|
||||
@@ -402,7 +421,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
|
||||
}
|
||||
|
||||
bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
|
||||
const StringRef attribute_name) const
|
||||
const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
@@ -411,7 +430,8 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
for (const int i : IndexRange(custom_data->totlayer)) {
|
||||
const CustomDataLayer &layer = custom_data->layers[i];
|
||||
if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) {
|
||||
if (this->type_is_supported((CustomDataType)layer.type) &&
|
||||
custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
CustomData_free_layer(custom_data, layer.type, domain_size, i);
|
||||
return true;
|
||||
}
|
||||
@@ -419,24 +439,39 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name,
|
||||
CustomData &custom_data,
|
||||
const CustomDataType data_type,
|
||||
const int domain_size,
|
||||
const AttributeInit &initializer)
|
||||
static void *add_generic_custom_data_layer(CustomData &custom_data,
|
||||
const CustomDataType data_type,
|
||||
const eCDAllocType alloctype,
|
||||
void *layer_data,
|
||||
const int domain_size,
|
||||
const AttributeIDRef &attribute_id)
|
||||
{
|
||||
char attribute_name_c[MAX_NAME];
|
||||
attribute_name.copy(attribute_name_c);
|
||||
if (attribute_id.is_named()) {
|
||||
char attribute_name_c[MAX_NAME];
|
||||
attribute_id.name().copy(attribute_name_c);
|
||||
return CustomData_add_layer_named(
|
||||
&custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
|
||||
}
|
||||
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
|
||||
return CustomData_add_layer_anonymous(
|
||||
&custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id);
|
||||
}
|
||||
|
||||
static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id,
|
||||
CustomData &custom_data,
|
||||
const CustomDataType data_type,
|
||||
const int domain_size,
|
||||
const AttributeInit &initializer)
|
||||
{
|
||||
switch (initializer.type) {
|
||||
case AttributeInit::Type::Default: {
|
||||
void *data = CustomData_add_layer_named(
|
||||
&custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
|
||||
void *data = add_generic_custom_data_layer(
|
||||
custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id);
|
||||
return data != nullptr;
|
||||
}
|
||||
case AttributeInit::Type::VArray: {
|
||||
void *data = CustomData_add_layer_named(
|
||||
&custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
|
||||
void *data = add_generic_custom_data_layer(
|
||||
custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id);
|
||||
if (data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -446,8 +481,8 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr
|
||||
}
|
||||
case AttributeInit::Type::MoveArray: {
|
||||
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
|
||||
void *data = CustomData_add_layer_named(
|
||||
&custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c);
|
||||
void *data = add_generic_custom_data_layer(
|
||||
custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id);
|
||||
if (data == nullptr) {
|
||||
MEM_freeN(source_data);
|
||||
return false;
|
||||
@@ -461,7 +496,7 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr
|
||||
}
|
||||
|
||||
bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
|
||||
const StringRef attribute_name,
|
||||
const AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer) const
|
||||
@@ -477,13 +512,13 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
|
||||
return false;
|
||||
}
|
||||
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
||||
if (layer.name == attribute_name) {
|
||||
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
add_named_custom_data_layer_from_attribute_init(
|
||||
attribute_name, *custom_data, data_type, domain_size, initializer);
|
||||
add_custom_data_layer_from_attribute_init(
|
||||
attribute_id, *custom_data, data_type, domain_size, initializer);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -498,7 +533,14 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
|
||||
const CustomDataType data_type = (CustomDataType)layer.type;
|
||||
if (this->type_is_supported(data_type)) {
|
||||
AttributeMetaData meta_data{domain_, data_type};
|
||||
if (!callback(layer.name, meta_data)) {
|
||||
AttributeIDRef attribute_id;
|
||||
if (layer.anonymous_id != nullptr) {
|
||||
attribute_id = layer.anonymous_id;
|
||||
}
|
||||
else {
|
||||
attribute_id = layer.name;
|
||||
}
|
||||
if (!callback(attribute_id, meta_data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -507,7 +549,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
|
||||
}
|
||||
|
||||
ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
|
||||
const GeometryComponent &component, const StringRef attribute_name) const
|
||||
const GeometryComponent &component, const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
@@ -515,7 +557,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
|
||||
}
|
||||
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
||||
if (layer.type == stored_type_) {
|
||||
if (layer.name == attribute_name) {
|
||||
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
return {as_read_attribute_(layer.data, domain_size), domain_};
|
||||
}
|
||||
@@ -525,7 +567,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
|
||||
}
|
||||
|
||||
WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
|
||||
GeometryComponent &component, const StringRef attribute_name) const
|
||||
GeometryComponent &component, const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
@@ -533,7 +575,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
|
||||
}
|
||||
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
|
||||
if (layer.type == stored_type_) {
|
||||
if (layer.name == attribute_name) {
|
||||
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
void *data_old = layer.data;
|
||||
void *data_new = CustomData_duplicate_referenced_layer_named(
|
||||
@@ -549,7 +591,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
|
||||
}
|
||||
|
||||
bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component,
|
||||
const StringRef attribute_name) const
|
||||
const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
@@ -558,7 +600,7 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component,
|
||||
for (const int i : IndexRange(custom_data->totlayer)) {
|
||||
const CustomDataLayer &layer = custom_data->layers[i];
|
||||
if (layer.type == stored_type_) {
|
||||
if (layer.name == attribute_name) {
|
||||
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
CustomData_free_layer(custom_data, stored_type_, domain_size, i);
|
||||
custom_data_access_.update_custom_data_pointers(component);
|
||||
@@ -627,11 +669,11 @@ CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
|
||||
std::optional<GSpan> CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
BLI_assert(size_ != 0);
|
||||
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
|
||||
if (layer.name == name) {
|
||||
if (custom_data_layer_matches_attribute_id(layer, attribute_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_);
|
||||
@@ -645,13 +687,13 @@ std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) co
|
||||
* value if the attribute doesn't exist. If no default value is provided, the default value for the
|
||||
* type will be used.
|
||||
*/
|
||||
GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name,
|
||||
GVArrayPtr CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const
|
||||
{
|
||||
const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
||||
|
||||
std::optional<GSpan> attribute = this->get_for_read(name);
|
||||
std::optional<GSpan> attribute = this->get_for_read(attribute_id);
|
||||
if (!attribute) {
|
||||
const int domain_size = this->size_;
|
||||
return std::make_unique<GVArray_For_SingleValue>(
|
||||
@@ -666,12 +708,12 @@ GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name,
|
||||
return conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), *type);
|
||||
}
|
||||
|
||||
std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
|
||||
std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const AttributeIDRef &attribute_id)
|
||||
{
|
||||
/* If this assert hits, it most likely means that #reallocate was not called at some point. */
|
||||
BLI_assert(size_ != 0);
|
||||
for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
|
||||
if (layer.name == name) {
|
||||
if (custom_data_layer_matches_attribute_id(layer, attribute_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_);
|
||||
@@ -680,30 +722,29 @@ std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type)
|
||||
bool CustomDataAttributes::create(const AttributeIDRef &attribute_id,
|
||||
const CustomDataType data_type)
|
||||
{
|
||||
char name_c[MAX_NAME];
|
||||
name.copy(name_c);
|
||||
void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c);
|
||||
void *result = add_generic_custom_data_layer(
|
||||
data, data_type, CD_DEFAULT, nullptr, size_, attribute_id);
|
||||
return result != nullptr;
|
||||
}
|
||||
|
||||
bool CustomDataAttributes::create_by_move(const blender::StringRef name,
|
||||
bool CustomDataAttributes::create_by_move(const blender::bke::AttributeIDRef &attribute_id,
|
||||
const CustomDataType data_type,
|
||||
void *buffer)
|
||||
{
|
||||
char name_c[MAX_NAME];
|
||||
name.copy(name_c);
|
||||
void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c);
|
||||
void *result = add_generic_custom_data_layer(
|
||||
data, data_type, CD_ASSIGN, buffer, size_, attribute_id);
|
||||
return result != nullptr;
|
||||
}
|
||||
|
||||
bool CustomDataAttributes::remove(const blender::StringRef name)
|
||||
bool CustomDataAttributes::remove(const blender::bke::AttributeIDRef &attribute_id)
|
||||
{
|
||||
bool result = false;
|
||||
for (const int i : IndexRange(data.totlayer)) {
|
||||
const CustomDataLayer &layer = data.layers[i];
|
||||
if (layer.name == name) {
|
||||
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
CustomData_free_layer(&data, layer.type, size_, i);
|
||||
result = true;
|
||||
}
|
||||
@@ -722,7 +763,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
|
||||
{
|
||||
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
|
||||
AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
|
||||
if (!callback(layer.name, meta_data)) {
|
||||
AttributeIDRef attribute_id;
|
||||
if (layer.anonymous_id != nullptr) {
|
||||
attribute_id = layer.anonymous_id;
|
||||
}
|
||||
else {
|
||||
attribute_id = layer.name;
|
||||
}
|
||||
if (!callback(attribute_id, meta_data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -766,21 +814,23 @@ bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_
|
||||
}
|
||||
|
||||
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
|
||||
const StringRef attribute_name) const
|
||||
const blender::bke::AttributeIDRef &attribute_id) const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
if (providers == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
|
||||
if (attribute_id.is_named()) {
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
|
||||
}
|
||||
}
|
||||
for (const DynamicAttributesProvider *dynamic_provider :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
|
||||
ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_id);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
@@ -800,21 +850,23 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_dom
|
||||
}
|
||||
|
||||
blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write(
|
||||
const StringRef attribute_name)
|
||||
const blender::bke::AttributeIDRef &attribute_id)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
if (providers == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
|
||||
if (attribute_id.is_named()) {
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
|
||||
}
|
||||
}
|
||||
for (const DynamicAttributesProvider *dynamic_provider :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
|
||||
WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_id);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
@@ -822,53 +874,57 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ
|
||||
return {};
|
||||
}
|
||||
|
||||
bool GeometryComponent::attribute_try_delete(const StringRef attribute_name)
|
||||
bool GeometryComponent::attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
if (providers == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
return builtin_provider->try_delete(*this);
|
||||
if (attribute_id.is_named()) {
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
return builtin_provider->try_delete(*this);
|
||||
}
|
||||
}
|
||||
bool success = false;
|
||||
for (const DynamicAttributesProvider *dynamic_provider :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
success = dynamic_provider->try_delete(*this, attribute_name) || success;
|
||||
success = dynamic_provider->try_delete(*this, attribute_id) || success;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
|
||||
bool GeometryComponent::attribute_try_create(const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
if (attribute_name.is_empty()) {
|
||||
if (!attribute_id) {
|
||||
return false;
|
||||
}
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
if (providers == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
if (builtin_provider->domain() != domain) {
|
||||
return false;
|
||||
if (attribute_id.is_named()) {
|
||||
const BuiltinAttributeProvider *builtin_provider =
|
||||
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
|
||||
if (builtin_provider != nullptr) {
|
||||
if (builtin_provider->domain() != domain) {
|
||||
return false;
|
||||
}
|
||||
if (builtin_provider->data_type() != data_type) {
|
||||
return false;
|
||||
}
|
||||
return builtin_provider->try_create(*this, initializer);
|
||||
}
|
||||
if (builtin_provider->data_type() != data_type) {
|
||||
return false;
|
||||
}
|
||||
return builtin_provider->try_create(*this, initializer);
|
||||
}
|
||||
for (const DynamicAttributesProvider *dynamic_provider :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) {
|
||||
if (dynamic_provider->try_create(*this, attribute_id, domain, data_type, initializer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -894,11 +950,12 @@ bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef at
|
||||
return builtin_provider->try_create(*this, initializer);
|
||||
}
|
||||
|
||||
Set<std::string> GeometryComponent::attribute_names() const
|
||||
Set<blender::bke::AttributeIDRef> GeometryComponent::attribute_ids() const
|
||||
{
|
||||
Set<std::string> attributes;
|
||||
this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) {
|
||||
attributes.add(name);
|
||||
Set<blender::bke::AttributeIDRef> attributes;
|
||||
this->attribute_foreach([&](const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeMetaData &UNUSED(meta_data)) {
|
||||
attributes.add(attribute_id);
|
||||
return true;
|
||||
});
|
||||
return attributes;
|
||||
@@ -931,9 +988,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
|
||||
}
|
||||
for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) {
|
||||
const bool continue_loop = provider->foreach_attribute(
|
||||
*this, [&](StringRefNull name, const AttributeMetaData &meta_data) {
|
||||
if (handled_attribute_names.add(name)) {
|
||||
return callback(name, meta_data);
|
||||
*this, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
if (attribute_id.is_anonymous() || handled_attribute_names.add(attribute_id.name())) {
|
||||
return callback(attribute_id, meta_data);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -945,9 +1002,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
|
||||
bool GeometryComponent::attribute_exists(const blender::bke::AttributeIDRef &attribute_id) const
|
||||
{
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
|
||||
if (attribute) {
|
||||
return true;
|
||||
}
|
||||
@@ -955,11 +1012,12 @@ bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name
|
||||
}
|
||||
|
||||
std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
|
||||
const StringRef attribute_name) const
|
||||
const blender::bke::AttributeIDRef &attribute_id) const
|
||||
{
|
||||
std::optional<AttributeMetaData> result{std::nullopt};
|
||||
this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
|
||||
if (attribute_name == name) {
|
||||
this->attribute_foreach([&](const blender::bke::AttributeIDRef ¤t_attribute_id,
|
||||
const AttributeMetaData &meta_data) {
|
||||
if (attribute_id == current_attribute_id) {
|
||||
result = meta_data;
|
||||
return false;
|
||||
}
|
||||
@@ -977,11 +1035,11 @@ static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
|
||||
}
|
||||
|
||||
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
|
||||
const StringRef attribute_name,
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const
|
||||
{
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
@@ -1007,13 +1065,13 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_r
|
||||
}
|
||||
|
||||
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read(
|
||||
const StringRef attribute_name, const AttributeDomain domain) const
|
||||
const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) const
|
||||
{
|
||||
if (!this->attribute_domain_supported(domain)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
@@ -1026,9 +1084,9 @@ std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_
|
||||
}
|
||||
|
||||
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name, const CustomDataType data_type) const
|
||||
const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type) const
|
||||
{
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
@@ -1043,13 +1101,13 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
|
||||
}
|
||||
|
||||
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
|
||||
const StringRef attribute_name,
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const
|
||||
{
|
||||
std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read(
|
||||
attribute_name, domain, data_type);
|
||||
attribute_id, domain, data_type);
|
||||
if (varray) {
|
||||
return varray;
|
||||
}
|
||||
@@ -1065,15 +1123,22 @@ class GVMutableAttribute_For_OutputAttribute
|
||||
: public blender::fn::GVMutableArray_For_GMutableSpan {
|
||||
public:
|
||||
GeometryComponent *component;
|
||||
std::string final_name;
|
||||
std::string attribute_name;
|
||||
blender::bke::WeakAnonymousAttributeID anonymous_attribute_id;
|
||||
|
||||
GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
|
||||
GeometryComponent &component,
|
||||
std::string final_name)
|
||||
: blender::fn::GVMutableArray_For_GMutableSpan(data),
|
||||
component(&component),
|
||||
final_name(std::move(final_name))
|
||||
const blender::bke::AttributeIDRef &attribute_id)
|
||||
: blender::fn::GVMutableArray_For_GMutableSpan(data), component(&component)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
this->attribute_name = attribute_id.name();
|
||||
}
|
||||
else {
|
||||
const AnonymousAttributeID *anonymous_id = &attribute_id.anonymous_id();
|
||||
BKE_anonymous_attribute_id_increment_weak(anonymous_id);
|
||||
this->anonymous_attribute_id = blender::bke::WeakAnonymousAttributeID{anonymous_id};
|
||||
}
|
||||
}
|
||||
|
||||
~GVMutableAttribute_For_OutputAttribute() override
|
||||
@@ -1093,21 +1158,28 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut
|
||||
dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray());
|
||||
|
||||
GeometryComponent &component = *varray.component;
|
||||
const StringRefNull name = varray.final_name;
|
||||
AttributeIDRef attribute_id;
|
||||
if (!varray.attribute_name.empty()) {
|
||||
attribute_id = varray.attribute_name;
|
||||
}
|
||||
else {
|
||||
attribute_id = varray.anonymous_attribute_id.extract();
|
||||
}
|
||||
const AttributeDomain domain = output_attribute.domain();
|
||||
const CustomDataType data_type = output_attribute.custom_data_type();
|
||||
const CPPType &cpp_type = output_attribute.cpp_type();
|
||||
|
||||
component.attribute_try_delete(name);
|
||||
if (!component.attribute_try_create(
|
||||
varray.final_name, domain, data_type, AttributeInitDefault())) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Could not create the '%s' attribute with type '%s'.",
|
||||
name.c_str(),
|
||||
cpp_type.name().c_str());
|
||||
component.attribute_try_delete(attribute_id);
|
||||
if (!component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault())) {
|
||||
if (!varray.attribute_name.empty()) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Could not create the '%s' attribute with type '%s'.",
|
||||
varray.attribute_name.c_str(),
|
||||
cpp_type.name().c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
|
||||
WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id);
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
|
||||
for (const int i : IndexRange(varray.size())) {
|
||||
varray.get(i, buffer);
|
||||
@@ -1117,7 +1189,7 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut
|
||||
|
||||
static blender::bke::OutputAttribute create_output_attribute(
|
||||
GeometryComponent &component,
|
||||
const blender::StringRef attribute_name,
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const bool ignore_old_values,
|
||||
@@ -1127,7 +1199,7 @@ static blender::bke::OutputAttribute create_output_attribute(
|
||||
using namespace blender::fn;
|
||||
using namespace blender::bke;
|
||||
|
||||
if (attribute_name.is_empty()) {
|
||||
if (!attribute_id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1135,7 +1207,8 @@ static blender::bke::OutputAttribute create_output_attribute(
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
|
||||
|
||||
if (component.attribute_is_builtin(attribute_name)) {
|
||||
if (attribute_id.is_named() && component.attribute_is_builtin(attribute_id.name())) {
|
||||
const StringRef attribute_name = attribute_id.name();
|
||||
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
|
||||
if (!attribute) {
|
||||
if (default_value) {
|
||||
@@ -1169,18 +1242,18 @@ static blender::bke::OutputAttribute create_output_attribute(
|
||||
|
||||
const int domain_size = component.attribute_domain_size(domain);
|
||||
|
||||
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
|
||||
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id);
|
||||
if (!attribute) {
|
||||
if (default_value) {
|
||||
const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
|
||||
component.attribute_try_create(
|
||||
attribute_name, domain, data_type, AttributeInitVArray(&default_varray));
|
||||
attribute_id, domain, data_type, AttributeInitVArray(&default_varray));
|
||||
}
|
||||
else {
|
||||
component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault());
|
||||
component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault());
|
||||
}
|
||||
|
||||
attribute = component.attribute_try_get_for_write(attribute_name);
|
||||
attribute = component.attribute_try_get_for_write(attribute_id);
|
||||
if (!attribute) {
|
||||
/* Can't create the attribute. */
|
||||
return {};
|
||||
@@ -1202,28 +1275,28 @@ static blender::bke::OutputAttribute create_output_attribute(
|
||||
else {
|
||||
/* Fill the temporary array with values from the existing attribute. */
|
||||
GVArrayPtr old_varray = component.attribute_get_for_read(
|
||||
attribute_name, domain, data_type, default_value);
|
||||
attribute_id, domain, data_type, default_value);
|
||||
old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
|
||||
}
|
||||
GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>(
|
||||
GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name);
|
||||
GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id);
|
||||
|
||||
return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
|
||||
}
|
||||
|
||||
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output(
|
||||
const StringRef attribute_name,
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value)
|
||||
{
|
||||
return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value);
|
||||
return create_output_attribute(*this, attribute_id, domain, data_type, false, default_value);
|
||||
}
|
||||
|
||||
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
|
||||
const blender::StringRef attribute_name,
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type)
|
||||
{
|
||||
return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
|
||||
return create_output_attribute(*this, attribute_id, domain, data_type, true, nullptr);
|
||||
}
|
||||
|
@@ -116,12 +116,13 @@ class BuiltinAttributeProvider {
|
||||
class DynamicAttributesProvider {
|
||||
public:
|
||||
virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
|
||||
const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual bool try_delete(GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual bool try_create(GeometryComponent &UNUSED(component),
|
||||
const StringRef UNUSED(attribute_name),
|
||||
const AttributeIDRef &UNUSED(attribute_id),
|
||||
const AttributeDomain UNUSED(domain),
|
||||
const CustomDataType UNUSED(data_type),
|
||||
const AttributeInit &UNUSED(initializer)) const
|
||||
@@ -154,15 +155,15 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
}
|
||||
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
|
||||
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final;
|
||||
|
||||
bool try_create(GeometryComponent &component,
|
||||
const StringRef attribute_name,
|
||||
const AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer) const final;
|
||||
@@ -231,10 +232,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
||||
}
|
||||
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final;
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final;
|
||||
void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final;
|
||||
|
@@ -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");
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
@@ -330,13 +331,13 @@ void CurveEval::assert_valid_point_attributes() const
|
||||
return;
|
||||
}
|
||||
const int layer_len = splines_.first()->attributes.data.totlayer;
|
||||
Map<StringRefNull, AttributeMetaData> map;
|
||||
Map<blender::bke::AttributeIDRef, AttributeMetaData> map;
|
||||
for (const SplinePtr &spline : splines_) {
|
||||
BLI_assert(spline->attributes.data.totlayer == layer_len);
|
||||
spline->attributes.foreach_attribute(
|
||||
[&](StringRefNull name, const AttributeMetaData &meta_data) {
|
||||
[&](const blender::bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
map.add_or_modify(
|
||||
name,
|
||||
attribute_id,
|
||||
[&](AttributeMetaData *map_data) {
|
||||
/* All unique attribute names should be added on the first spline. */
|
||||
BLI_assert(spline == splines_.first());
|
||||
|
@@ -46,6 +46,7 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_anonymous_attribute.h"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_customdata_file.h"
|
||||
#include "BKE_deform.h"
|
||||
@@ -2127,6 +2128,10 @@ bool CustomData_merge(const struct CustomData *source,
|
||||
if (flag & CD_FLAG_NOCOPY) {
|
||||
continue;
|
||||
}
|
||||
if (layer->anonymous_id &&
|
||||
!BKE_anonymous_attribute_id_has_strong_references(layer->anonymous_id)) {
|
||||
continue;
|
||||
}
|
||||
if (!(mask & CD_TYPE_AS_MASK(type))) {
|
||||
continue;
|
||||
}
|
||||
@@ -2166,6 +2171,11 @@ bool CustomData_merge(const struct CustomData *source,
|
||||
newlayer->active_mask = lastmask;
|
||||
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
|
||||
changed = true;
|
||||
|
||||
if (layer->anonymous_id != NULL) {
|
||||
BKE_anonymous_attribute_id_increment_weak(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->anonymous_id != NULL) {
|
||||
BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id);
|
||||
layer->anonymous_id = NULL;
|
||||
}
|
||||
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
|
||||
typeInfo = layerType_getInfo(layer->type);
|
||||
|
||||
@@ -2649,6 +2663,27 @@ void *CustomData_add_layer_named(CustomData *data,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *CustomData_add_layer_anonymous(struct CustomData *data,
|
||||
int type,
|
||||
eCDAllocType alloctype,
|
||||
void *layerdata,
|
||||
int totelem,
|
||||
const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id);
|
||||
CustomDataLayer *layer = customData_add_layer__internal(
|
||||
data, type, alloctype, layerdata, totelem, name);
|
||||
CustomData_update_typemap(data);
|
||||
|
||||
if (layer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BKE_anonymous_attribute_id_increment_weak(anonymous_id);
|
||||
layer->anonymous_id = anonymous_id;
|
||||
return layer->data;
|
||||
}
|
||||
|
||||
bool CustomData_free_layer(CustomData *data, int type, int totelem, int index)
|
||||
{
|
||||
const int index_first = CustomData_get_layer_index(data, type);
|
||||
@@ -2812,6 +2847,20 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data,
|
||||
return customData_duplicate_referenced_layer_index(data, layer_index, totelem);
|
||||
}
|
||||
|
||||
void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data,
|
||||
const int type,
|
||||
const AnonymousAttributeID *anonymous_id,
|
||||
const int totelem)
|
||||
{
|
||||
for (int i = 0; i < data->totlayer; i++) {
|
||||
if (data->layers[i].anonymous_id == anonymous_id) {
|
||||
return customData_duplicate_referenced_layer_index(data, i, totelem);
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CustomData_duplicate_referenced_layers(CustomData *data, int totelem)
|
||||
{
|
||||
for (int i = 0; i < data->totlayer; i++) {
|
||||
@@ -4244,7 +4293,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) || layer->anonymous_id != NULL) {
|
||||
data->totlayer--;
|
||||
// CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name);
|
||||
}
|
||||
|
@@ -892,7 +892,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
|
||||
public:
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
@@ -902,13 +902,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
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_for_read(attribute_name);
|
||||
std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_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_for_read(attribute_name);
|
||||
std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_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
|
||||
@@ -945,7 +945,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
|
||||
/* This function is almost the same as #try_get_for_read, but without const. */
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
@@ -955,13 +955,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
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_for_write(attribute_name);
|
||||
std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_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_for_write(attribute_name);
|
||||
std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_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
|
||||
@@ -996,7 +996,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
return attribute;
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
|
||||
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
if (curve == nullptr) {
|
||||
@@ -1006,7 +1006,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
/* Reuse the boolean for all splines; we expect all splines to have the same attributes. */
|
||||
bool layer_freed = false;
|
||||
for (SplinePtr &spline : curve->splines()) {
|
||||
layer_freed = spline->attributes.remove(attribute_name);
|
||||
layer_freed = spline->attributes.remove(attribute_id);
|
||||
}
|
||||
return layer_freed;
|
||||
}
|
||||
@@ -1034,7 +1034,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &component,
|
||||
const StringRef attribute_name,
|
||||
const AttributeIDRef &attribute_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer) const final
|
||||
@@ -1053,7 +1053,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
/* First check the one case that allows us to avoid copying the input data. */
|
||||
if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
|
||||
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
|
||||
if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) {
|
||||
if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) {
|
||||
MEM_freeN(source_data);
|
||||
return false;
|
||||
}
|
||||
@@ -1062,7 +1062,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
|
||||
/* Otherwise just create a custom data layer on each of the splines. */
|
||||
for (const int i : splines.index_range()) {
|
||||
if (!splines[i]->attributes.create(attribute_name, data_type)) {
|
||||
if (!splines[i]->attributes.create(attribute_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. */
|
||||
@@ -1076,7 +1076,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name);
|
||||
WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id);
|
||||
/* We just created the attribute, it should exist. */
|
||||
BLI_assert(write_attribute);
|
||||
|
||||
|
@@ -818,16 +818,20 @@ class VArray_For_VertexWeights final : public VArray<float> {
|
||||
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
public:
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
if (!attribute_id.is_named()) {
|
||||
return {};
|
||||
}
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
const Mesh *mesh = mesh_component.get_for_read();
|
||||
if (mesh == nullptr) {
|
||||
return {};
|
||||
}
|
||||
std::string name = attribute_id.name();
|
||||
const int vertex_group_index = BLI_findstringindex(
|
||||
&mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name));
|
||||
&mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name));
|
||||
if (vertex_group_index < 0) {
|
||||
return {};
|
||||
}
|
||||
@@ -843,17 +847,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
if (!attribute_id.is_named()) {
|
||||
return {};
|
||||
}
|
||||
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
||||
Mesh *mesh = mesh_component.get_for_write();
|
||||
if (mesh == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string name = attribute_id.name();
|
||||
const int vertex_group_index = BLI_findstringindex(
|
||||
&mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name));
|
||||
&mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name));
|
||||
if (vertex_group_index < 0) {
|
||||
return {};
|
||||
}
|
||||
@@ -872,17 +880,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
|
||||
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
if (!attribute_id.is_named()) {
|
||||
return false;
|
||||
}
|
||||
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
||||
Mesh *mesh = mesh_component.get_for_write();
|
||||
if (mesh == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string name = attribute_id.name();
|
||||
const int vertex_group_index = BLI_findstringindex(
|
||||
&mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name));
|
||||
&mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name));
|
||||
if (vertex_group_index < 0) {
|
||||
return false;
|
||||
}
|
||||
|
@@ -319,7 +319,7 @@ void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
|
||||
void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
|
||||
Span<GeometryComponentType> component_types,
|
||||
const Set<std::string> &ignored_attributes,
|
||||
Map<std::string, AttributeKind> &r_attributes)
|
||||
Map<AttributeIDRef, AttributeKind> &r_attributes)
|
||||
{
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
const GeometrySet &set = set_group.geometry_set;
|
||||
@@ -329,23 +329,24 @@ void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> se
|
||||
}
|
||||
const GeometryComponent &component = *set.get_component_for_read(component_type);
|
||||
|
||||
component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
|
||||
if (ignored_attributes.contains(name)) {
|
||||
return true;
|
||||
}
|
||||
auto add_info = [&](AttributeKind *attribute_kind) {
|
||||
attribute_kind->domain = meta_data.domain;
|
||||
attribute_kind->data_type = meta_data.data_type;
|
||||
};
|
||||
auto modify_info = [&](AttributeKind *attribute_kind) {
|
||||
attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */
|
||||
attribute_kind->data_type = bke::attribute_data_type_highest_complexity(
|
||||
{attribute_kind->data_type, meta_data.data_type});
|
||||
};
|
||||
component.attribute_foreach(
|
||||
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) {
|
||||
return true;
|
||||
}
|
||||
auto add_info = [&](AttributeKind *attribute_kind) {
|
||||
attribute_kind->domain = meta_data.domain;
|
||||
attribute_kind->data_type = meta_data.data_type;
|
||||
};
|
||||
auto modify_info = [&](AttributeKind *attribute_kind) {
|
||||
attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */
|
||||
attribute_kind->data_type = bke::attribute_data_type_highest_complexity(
|
||||
{attribute_kind->data_type, meta_data.data_type});
|
||||
};
|
||||
|
||||
r_attributes.add_or_modify(name, add_info, modify_info);
|
||||
return true;
|
||||
});
|
||||
r_attributes.add_or_modify(attribute_id, add_info, modify_info);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -500,11 +501,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
|
||||
|
||||
static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
Span<GeometryComponentType> component_types,
|
||||
const Map<std::string, AttributeKind> &attribute_info,
|
||||
const Map<AttributeIDRef, AttributeKind> &attribute_info,
|
||||
GeometryComponent &result)
|
||||
{
|
||||
for (Map<std::string, AttributeKind>::Item entry : attribute_info.items()) {
|
||||
StringRef name = entry.key;
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attribute_info.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
const AttributeDomain domain_output = entry.value.domain;
|
||||
const CustomDataType data_type_output = entry.value.data_type;
|
||||
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
|
||||
@@ -512,7 +513,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
|
||||
result.attribute_try_create(
|
||||
entry.key, domain_output, data_type_output, AttributeInitDefault());
|
||||
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
|
||||
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(attribute_id);
|
||||
if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
|
||||
write_attribute.domain != domain_output) {
|
||||
continue;
|
||||
@@ -531,7 +532,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
continue; /* Domain size is 0, so no need to increment the offset. */
|
||||
}
|
||||
GVArrayPtr source_attribute = component.attribute_try_get_for_read(
|
||||
name, domain_output, data_type_output);
|
||||
attribute_id, domain_output, data_type_output);
|
||||
|
||||
if (source_attribute) {
|
||||
fn::GVArray_GSpan src_span{*source_attribute};
|
||||
@@ -641,7 +642,7 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
|
||||
}
|
||||
|
||||
/* Don't copy attributes that are stored directly in the mesh data structs. */
|
||||
Map<std::string, AttributeKind> attributes;
|
||||
Map<AttributeIDRef, AttributeKind> attributes;
|
||||
geometry_set_gather_instances_attribute_info(
|
||||
set_groups,
|
||||
component_types,
|
||||
@@ -662,7 +663,7 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou
|
||||
PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
|
||||
dst_component.replace(new_pointcloud);
|
||||
|
||||
Map<std::string, AttributeKind> attributes;
|
||||
Map<AttributeIDRef, AttributeKind> attributes;
|
||||
geometry_set_gather_instances_attribute_info(
|
||||
set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {"position"}, attributes);
|
||||
join_attributes(set_groups,
|
||||
@@ -696,7 +697,7 @@ static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, G
|
||||
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
|
||||
dst_component.replace(curve);
|
||||
|
||||
Map<std::string, AttributeKind> attributes;
|
||||
Map<AttributeIDRef, AttributeKind> attributes;
|
||||
geometry_set_gather_instances_attribute_info(
|
||||
set_groups,
|
||||
{GEO_COMPONENT_TYPE_CURVE},
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -18,18 +18,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_BlurBaseOperation.h"
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_GaussianAlphaBlurBaseOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class GaussianAlphaYBlurOperation : public BlurBaseOperation {
|
||||
/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
|
||||
class GaussianAlphaYBlurOperation : public GaussianAlphaBlurBaseOperation {
|
||||
private:
|
||||
float *m_gausstab;
|
||||
float *m_distbuf_inv;
|
||||
bool m_do_subtract;
|
||||
int m_falloff;
|
||||
int m_filtersize;
|
||||
void updateGauss();
|
||||
|
||||
public:
|
||||
@@ -54,18 +49,6 @@ class GaussianAlphaYBlurOperation : 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
|
||||
|
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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_GaussianBlurBaseOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
GaussianBlurBaseOperation::GaussianBlurBaseOperation(eDimension dim)
|
||||
: BlurBaseOperation(DataType::Color)
|
||||
{
|
||||
m_gausstab = nullptr;
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
m_gausstab_sse = nullptr;
|
||||
#endif
|
||||
m_filtersize = 0;
|
||||
rad_ = 0.0f;
|
||||
dimension_ = dim;
|
||||
}
|
||||
|
||||
void GaussianBlurBaseOperation::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 GaussianBlurBaseOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize);
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GaussianBlurBaseOperation::deinitExecution()
|
||||
{
|
||||
BlurBaseOperation::deinitExecution();
|
||||
|
||||
if (m_gausstab) {
|
||||
MEM_freeN(m_gausstab);
|
||||
m_gausstab = nullptr;
|
||||
}
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
if (m_gausstab_sse) {
|
||||
MEM_freeN(m_gausstab_sse);
|
||||
m_gausstab_sse = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void GaussianBlurBaseOperation::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;
|
||||
}
|
||||
}
|
||||
|
||||
void GaussianBlurBaseOperation::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;
|
||||
elem_stride = input->elem_stride;
|
||||
get_current_coord = [&] { return it.x; };
|
||||
break;
|
||||
case eDimension::Y:
|
||||
min_input_coord = input_rect.ymin;
|
||||
max_input_coord = input_rect.ymax;
|
||||
elem_stride = input->row_stride;
|
||||
get_current_coord = [&] { return it.y; };
|
||||
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);
|
||||
|
||||
float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
float multiplier_accum = 0.0f;
|
||||
|
||||
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 gauss_idx = (coord_min - coord) + m_filtersize;
|
||||
const int gauss_end = gauss_idx + (coord_max - coord_min);
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
__m128 accum_r = _mm_load_ps(color_accum);
|
||||
for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) {
|
||||
__m128 reg_a = _mm_load_ps(in);
|
||||
reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[gauss_idx]);
|
||||
accum_r = _mm_add_ps(accum_r, reg_a);
|
||||
multiplier_accum += m_gausstab[gauss_idx];
|
||||
}
|
||||
_mm_store_ps(color_accum, accum_r);
|
||||
#else
|
||||
for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) {
|
||||
const float multiplier = m_gausstab[gauss_idx];
|
||||
madd_v4_v4fl(color_accum, in, multiplier);
|
||||
multiplier_accum += multiplier;
|
||||
}
|
||||
#endif
|
||||
mul_v4_v4fl(it.out, color_accum, 1.0f / multiplier_accum);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 GaussianBlurBaseOperation : public BlurBaseOperation {
|
||||
protected:
|
||||
float *m_gausstab;
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
__m128 *m_gausstab_sse;
|
||||
#endif
|
||||
int m_filtersize;
|
||||
float rad_;
|
||||
eDimension dimension_;
|
||||
|
||||
public:
|
||||
GaussianBlurBaseOperation(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) override;
|
||||
virtual void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
@@ -40,6 +40,27 @@ void *GaussianBokehBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void GaussianBokehBlurOperation::init_data()
|
||||
{
|
||||
BlurBaseOperation::init_data();
|
||||
const float width = this->getWidth();
|
||||
const float height = this->getHeight();
|
||||
|
||||
if (!this->m_sizeavailable) {
|
||||
updateSize();
|
||||
}
|
||||
|
||||
radxf_ = this->m_size * (float)this->m_data.sizex;
|
||||
CLAMP(radxf_, 0.0f, width / 2.0f);
|
||||
|
||||
/* Vertical. */
|
||||
radyf_ = this->m_size * (float)this->m_data.sizey;
|
||||
CLAMP(radyf_, 0.0f, height / 2.0f);
|
||||
|
||||
this->m_radx = ceil(radxf_);
|
||||
this->m_rady = ceil(radyf_);
|
||||
}
|
||||
|
||||
void GaussianBokehBlurOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
@@ -54,39 +75,17 @@ void GaussianBokehBlurOperation::initExecution()
|
||||
void GaussianBokehBlurOperation::updateGauss()
|
||||
{
|
||||
if (this->m_gausstab == nullptr) {
|
||||
float radxf;
|
||||
float radyf;
|
||||
int n;
|
||||
float *dgauss;
|
||||
float *ddgauss;
|
||||
int j, i;
|
||||
const float width = this->getWidth();
|
||||
const float height = this->getHeight();
|
||||
if (!this->m_sizeavailable) {
|
||||
updateSize();
|
||||
}
|
||||
radxf = this->m_size * (float)this->m_data.sizex;
|
||||
CLAMP(radxf, 0.0f, width / 2.0f);
|
||||
|
||||
/* vertical */
|
||||
radyf = this->m_size * (float)this->m_data.sizey;
|
||||
CLAMP(radyf, 0.0f, height / 2.0f);
|
||||
|
||||
this->m_radx = ceil(radxf);
|
||||
this->m_rady = ceil(radyf);
|
||||
|
||||
int ddwidth = 2 * this->m_radx + 1;
|
||||
int ddheight = 2 * this->m_rady + 1;
|
||||
n = ddwidth * ddheight;
|
||||
|
||||
int n = ddwidth * ddheight;
|
||||
/* create a full filter image */
|
||||
ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__);
|
||||
dgauss = ddgauss;
|
||||
float *ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__);
|
||||
float *dgauss = ddgauss;
|
||||
float sum = 0.0f;
|
||||
float facx = (radxf > 0.0f ? 1.0f / radxf : 0.0f);
|
||||
float facy = (radyf > 0.0f ? 1.0f / radyf : 0.0f);
|
||||
for (j = -this->m_rady; j <= this->m_rady; j++) {
|
||||
for (i = -this->m_radx; i <= this->m_radx; i++, dgauss++) {
|
||||
float facx = (radxf_ > 0.0f ? 1.0f / radxf_ : 0.0f);
|
||||
float facy = (radyf_ > 0.0f ? 1.0f / radyf_ : 0.0f);
|
||||
for (int j = -this->m_rady; j <= this->m_rady; j++) {
|
||||
for (int i = -this->m_radx; i <= this->m_radx; i++, dgauss++) {
|
||||
float fj = (float)j * facy;
|
||||
float fi = (float)i * facx;
|
||||
float dist = sqrt(fj * fj + fi * fi);
|
||||
@@ -99,7 +98,7 @@ void GaussianBokehBlurOperation::updateGauss()
|
||||
if (sum > 0.0f) {
|
||||
/* normalize */
|
||||
float norm = 1.0f / sum;
|
||||
for (j = n - 1; j >= 0; j--) {
|
||||
for (int j = n - 1; j >= 0; j--) {
|
||||
ddgauss[j] *= norm;
|
||||
}
|
||||
}
|
||||
@@ -196,23 +195,69 @@ bool GaussianBokehBlurOperation::determineDependingAreaOfInterest(
|
||||
return BlurBaseOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void GaussianBokehBlurOperation::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.xmax = output_area.xmax + m_radx;
|
||||
r_input_area.xmin = output_area.xmin - m_radx;
|
||||
r_input_area.ymax = output_area.ymax + m_rady;
|
||||
r_input_area.ymin = output_area.ymin - m_rady;
|
||||
}
|
||||
|
||||
void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
|
||||
BuffersIterator<float> it = output->iterate_with({}, area);
|
||||
const rcti &input_rect = input->get_rect();
|
||||
for (; !it.is_end(); ++it) {
|
||||
const int x = it.x;
|
||||
const int y = it.y;
|
||||
|
||||
const int ymin = max_ii(y - this->m_rady, input_rect.ymin);
|
||||
const int ymax = min_ii(y + this->m_rady + 1, input_rect.ymax);
|
||||
const int xmin = max_ii(x - this->m_radx, input_rect.xmin);
|
||||
const int xmax = min_ii(x + this->m_radx + 1, input_rect.xmax);
|
||||
|
||||
float tempColor[4] = {0};
|
||||
float multiplier_accum = 0;
|
||||
const int step = QualityStepHelper::getStep();
|
||||
const int elem_step = step * input->elem_stride;
|
||||
const int add_const = (xmin - x + this->m_radx);
|
||||
const int mul_const = (this->m_radx * 2 + 1);
|
||||
for (int ny = ymin; ny < ymax; ny += step) {
|
||||
const float *color = input->get_elem(xmin, ny);
|
||||
int gauss_index = ((ny - y) + this->m_rady) * mul_const + add_const;
|
||||
const int gauss_end = gauss_index + (xmax - xmin);
|
||||
for (; gauss_index < gauss_end; gauss_index += step, color += elem_step) {
|
||||
const float multiplier = this->m_gausstab[gauss_index];
|
||||
madd_v4_v4fl(tempColor, color, multiplier);
|
||||
multiplier_accum += multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
mul_v4_v4fl(it.out, tempColor, 1.0f / multiplier_accum);
|
||||
}
|
||||
}
|
||||
|
||||
// reference image
|
||||
GaussianBlurReferenceOperation::GaussianBlurReferenceOperation()
|
||||
: BlurBaseOperation(DataType::Color)
|
||||
{
|
||||
this->m_maintabs = nullptr;
|
||||
use_variable_size_ = true;
|
||||
}
|
||||
|
||||
void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/)
|
||||
void GaussianBlurReferenceOperation::init_data()
|
||||
{
|
||||
void *buffer = getInputOperation(0)->initializeTileData(nullptr);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void GaussianBlurReferenceOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
// setup gaustab
|
||||
/* Setup variables for gausstab and area of interest. */
|
||||
this->m_data.image_in_width = this->getWidth();
|
||||
this->m_data.image_in_height = this->getHeight();
|
||||
if (this->m_data.relative) {
|
||||
@@ -232,7 +277,7 @@ void GaussianBlurReferenceOperation::initExecution()
|
||||
}
|
||||
}
|
||||
|
||||
/* horizontal */
|
||||
/* Horizontal. */
|
||||
m_filtersizex = (float)this->m_data.sizex;
|
||||
int imgx = getWidth() / 2;
|
||||
if (m_filtersizex > imgx) {
|
||||
@@ -243,7 +288,7 @@ void GaussianBlurReferenceOperation::initExecution()
|
||||
}
|
||||
m_radx = (float)m_filtersizex;
|
||||
|
||||
/* vertical */
|
||||
/* Vertical. */
|
||||
m_filtersizey = (float)this->m_data.sizey;
|
||||
int imgy = getHeight() / 2;
|
||||
if (m_filtersizey > imgy) {
|
||||
@@ -253,6 +298,18 @@ void GaussianBlurReferenceOperation::initExecution()
|
||||
m_filtersizey = 1;
|
||||
}
|
||||
m_rady = (float)m_filtersizey;
|
||||
}
|
||||
|
||||
void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/)
|
||||
{
|
||||
void *buffer = getInputOperation(0)->initializeTileData(nullptr);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void GaussianBlurReferenceOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
|
||||
updateGauss();
|
||||
}
|
||||
|
||||
@@ -363,4 +420,78 @@ bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest(
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void GaussianBlurReferenceOperation::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;
|
||||
}
|
||||
|
||||
const int add_x = this->m_data.sizex + 2;
|
||||
const int add_y = this->m_data.sizey + 2;
|
||||
r_input_area.xmax = output_area.xmax + add_x;
|
||||
r_input_area.xmin = output_area.xmin - add_x;
|
||||
r_input_area.ymax = output_area.ymax + add_y;
|
||||
r_input_area.ymin = output_area.ymin - add_y;
|
||||
}
|
||||
|
||||
void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX];
|
||||
MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX];
|
||||
for (BuffersIterator<float> it = output->iterate_with({size_input}, area); !it.is_end(); ++it) {
|
||||
const float ref_size = *it.in(0);
|
||||
int ref_radx = (int)(ref_size * m_radx);
|
||||
int ref_rady = (int)(ref_size * m_rady);
|
||||
if (ref_radx > m_filtersizex) {
|
||||
ref_radx = m_filtersizex;
|
||||
}
|
||||
else if (ref_radx < 1) {
|
||||
ref_radx = 1;
|
||||
}
|
||||
if (ref_rady > m_filtersizey) {
|
||||
ref_rady = m_filtersizey;
|
||||
}
|
||||
else if (ref_rady < 1) {
|
||||
ref_rady = 1;
|
||||
}
|
||||
|
||||
const int x = it.x;
|
||||
const int y = it.y;
|
||||
if (ref_radx == 1 && ref_rady == 1) {
|
||||
image_input->read_elem(x, y, it.out);
|
||||
continue;
|
||||
}
|
||||
|
||||
const int w = getWidth();
|
||||
const int height = getHeight();
|
||||
const int minxr = x - ref_radx < 0 ? -x : -ref_radx;
|
||||
const int maxxr = x + ref_radx > w ? w - x : ref_radx;
|
||||
const int minyr = y - ref_rady < 0 ? -y : -ref_rady;
|
||||
const int maxyr = y + ref_rady > height ? height - y : ref_rady;
|
||||
|
||||
const float *gausstabx = m_maintabs[ref_radx - 1];
|
||||
const float *gausstabcentx = gausstabx + ref_radx;
|
||||
const float *gausstaby = m_maintabs[ref_rady - 1];
|
||||
const float *gausstabcenty = gausstaby + ref_rady;
|
||||
|
||||
float gauss_sum = 0.0f;
|
||||
float color_sum[4] = {0};
|
||||
const float *row_color = image_input->get_elem(x + minxr, y + minyr);
|
||||
for (int i = minyr; i < maxyr; i++, row_color += image_input->row_stride) {
|
||||
const float *color = row_color;
|
||||
for (int j = minxr; j < maxxr; j++, color += image_input->elem_stride) {
|
||||
const float val = gausstabcenty[i] * gausstabcentx[j];
|
||||
gauss_sum += val;
|
||||
madd_v4_v4fl(color_sum, color, val);
|
||||
}
|
||||
}
|
||||
mul_v4_v4fl(it.out, color_sum, 1.0f / gauss_sum);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -28,10 +28,13 @@ class GaussianBokehBlurOperation : public BlurBaseOperation {
|
||||
private:
|
||||
float *m_gausstab;
|
||||
int m_radx, m_rady;
|
||||
float radxf_;
|
||||
float radyf_;
|
||||
void updateGauss();
|
||||
|
||||
public:
|
||||
GaussianBokehBlurOperation();
|
||||
void init_data() override;
|
||||
void initExecution() override;
|
||||
void *initializeTileData(rcti *rect) override;
|
||||
/**
|
||||
@@ -47,6 +50,13 @@ class GaussianBokehBlurOperation : public BlurBaseOperation {
|
||||
bool determineDependingAreaOfInterest(rcti *input,
|
||||
ReadBufferOperation *readOperation,
|
||||
rcti *output) override;
|
||||
|
||||
void get_area_of_interest(const 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;
|
||||
};
|
||||
|
||||
class GaussianBlurReferenceOperation : public BlurBaseOperation {
|
||||
@@ -61,6 +71,7 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation {
|
||||
|
||||
public:
|
||||
GaussianBlurReferenceOperation();
|
||||
void init_data() override;
|
||||
void initExecution() override;
|
||||
void *initializeTileData(rcti *rect) override;
|
||||
/**
|
||||
@@ -76,6 +87,13 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation {
|
||||
bool determineDependingAreaOfInterest(rcti *input,
|
||||
ReadBufferOperation *readOperation,
|
||||
rcti *output) override;
|
||||
|
||||
void get_area_of_interest(const 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
|
||||
|
@@ -25,13 +25,8 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
GaussianXBlurOperation::GaussianXBlurOperation() : BlurBaseOperation(DataType::Color)
|
||||
GaussianXBlurOperation::GaussianXBlurOperation() : GaussianBlurBaseOperation(eDimension::X)
|
||||
{
|
||||
this->m_gausstab = nullptr;
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
this->m_gausstab_sse = nullptr;
|
||||
#endif
|
||||
this->m_filtersize = 0;
|
||||
}
|
||||
|
||||
void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
@@ -45,13 +40,14 @@ void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
void GaussianXBlurOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
GaussianBlurBaseOperation::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);
|
||||
|
||||
@@ -63,6 +59,7 @@ void GaussianXBlurOperation::initExecution()
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
void GaussianXBlurOperation::updateGauss()
|
||||
{
|
||||
if (this->m_gausstab == nullptr) {
|
||||
@@ -158,7 +155,7 @@ void GaussianXBlurOperation::executeOpenCL(OpenCLDevice *device,
|
||||
|
||||
void GaussianXBlurOperation::deinitExecution()
|
||||
{
|
||||
BlurBaseOperation::deinitExecution();
|
||||
GaussianBlurBaseOperation::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_GaussianBlurBaseOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class GaussianXBlurOperation : public BlurBaseOperation {
|
||||
/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
|
||||
class GaussianXBlurOperation : public GaussianBlurBaseOperation {
|
||||
private:
|
||||
float *m_gausstab;
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
__m128 *m_gausstab_sse;
|
||||
#endif
|
||||
int m_filtersize;
|
||||
void updateGauss();
|
||||
|
||||
public:
|
||||
|
@@ -25,13 +25,8 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
GaussianYBlurOperation::GaussianYBlurOperation() : BlurBaseOperation(DataType::Color)
|
||||
GaussianYBlurOperation::GaussianYBlurOperation() : GaussianBlurBaseOperation(eDimension::Y)
|
||||
{
|
||||
this->m_gausstab = nullptr;
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
this->m_gausstab_sse = nullptr;
|
||||
#endif
|
||||
this->m_filtersize = 0;
|
||||
}
|
||||
|
||||
void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
@@ -47,11 +42,11 @@ void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
|
||||
void GaussianYBlurOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
GaussianBlurBaseOperation::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);
|
||||
|
||||
@@ -158,7 +153,7 @@ void GaussianYBlurOperation::executeOpenCL(OpenCLDevice *device,
|
||||
|
||||
void GaussianYBlurOperation::deinitExecution()
|
||||
{
|
||||
BlurBaseOperation::deinitExecution();
|
||||
GaussianBlurBaseOperation::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_GaussianBlurBaseOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class GaussianYBlurOperation : public BlurBaseOperation {
|
||||
/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
|
||||
class GaussianYBlurOperation : public GaussianBlurBaseOperation {
|
||||
private:
|
||||
float *m_gausstab;
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
__m128 *m_gausstab_sse;
|
||||
#endif
|
||||
int m_filtersize;
|
||||
void updateGauss();
|
||||
|
||||
public:
|
||||
|
@@ -96,4 +96,67 @@ bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void KeyingBlurOperation::get_area_of_interest(const int UNUSED(input_idx),
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (m_axis) {
|
||||
case BLUR_AXIS_X:
|
||||
r_input_area.xmin = output_area.xmin - m_size;
|
||||
r_input_area.ymin = output_area.ymin;
|
||||
r_input_area.xmax = output_area.xmax + m_size;
|
||||
r_input_area.ymax = output_area.ymax;
|
||||
break;
|
||||
case BLUR_AXIS_Y:
|
||||
r_input_area.xmin = output_area.xmin;
|
||||
r_input_area.ymin = output_area.ymin - m_size;
|
||||
r_input_area.xmax = output_area.xmax;
|
||||
r_input_area.ymax = output_area.ymax + m_size;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_msg(0, "Unknown axis");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyingBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
BuffersIterator<float> it = output->iterate_with(inputs, area);
|
||||
|
||||
int coord_max;
|
||||
int elem_stride;
|
||||
std::function<int()> get_current_coord;
|
||||
switch (m_axis) {
|
||||
case BLUR_AXIS_X:
|
||||
get_current_coord = [&] { return it.x; };
|
||||
coord_max = this->getWidth();
|
||||
elem_stride = input->elem_stride;
|
||||
break;
|
||||
case BLUR_AXIS_Y:
|
||||
get_current_coord = [&] { return it.y; };
|
||||
coord_max = this->getHeight();
|
||||
elem_stride = input->row_stride;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; !it.is_end(); ++it) {
|
||||
const int coord = get_current_coord();
|
||||
const int start_coord = MAX2(0, coord - m_size + 1);
|
||||
const int end_coord = MIN2(coord_max, coord + m_size);
|
||||
const int count = end_coord - start_coord;
|
||||
|
||||
float sum = 0.0f;
|
||||
const float *start = it.in(0) + (start_coord - coord) * elem_stride;
|
||||
const float *end = start + count * elem_stride;
|
||||
for (const float *elem = start; elem < end; elem += elem_stride) {
|
||||
sum += *elem;
|
||||
}
|
||||
|
||||
*it.out = sum / count;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user