Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenlib')
-rw-r--r--source/blender/blenlib/BLI_color.hh302
-rw-r--r--source/blender/blenlib/BLI_index_mask.hh2
-rw-r--r--source/blender/blenlib/BLI_math_base.h3
-rw-r--r--source/blender/blenlib/BLI_mpq3.hh16
-rw-r--r--source/blender/blenlib/BLI_vector.hh2
-rw-r--r--source/blender/blenlib/CMakeLists.txt2
-rw-r--r--source/blender/blenlib/intern/BLI_color.cc55
-rw-r--r--source/blender/blenlib/intern/fileops.c4
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c15
-rw-r--r--source/blender/blenlib/intern/memory_utils.c2
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc223
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc91
-rw-r--r--source/blender/blenlib/intern/polyfill_2d_beautify.c2
-rw-r--r--source/blender/blenlib/intern/timecode.c4
-rw-r--r--source/blender/blenlib/intern/winstuff.c4
-rw-r--r--source/blender/blenlib/tests/BLI_color_test.cc133
16 files changed, 713 insertions, 147 deletions
diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh
index e57a5109a66..3b01bbfb86e 100644
--- a/source/blender/blenlib/BLI_color.hh
+++ b/source/blender/blenlib/BLI_color.hh
@@ -22,41 +22,122 @@
namespace blender {
-struct Color4f {
- float r, g, b, a;
+/**
+ * CPP based color structures.
+ *
+ * Strongly typed color storage structures with space and alpha association.
+ * Will increase readability and visibility of typical mistakes when
+ * working with colors.
+ *
+ * The storage structs can hold 4 channels (r, g, b and a).
+ *
+ * Usage:
+ *
+ * Convert a theme byte color to a linearrgb premultiplied.
+ * ```
+ * ColorTheme4b theme_color;
+ * ColorSceneLinear4f<eAlpha::Premultiplied> linearrgb_color =
+ * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha();
+ * ```
+ *
+ * The API is structured to make most use of inlining. Most notable are space
+ * conversions done via `BLI_color_convert_to*` functions.
+ *
+ * - Conversions between spaces (theme <=> scene linear) should always be done by
+ * invoking the `BLI_color_convert_to*` methods.
+ * - Encoding colors (compressing to store colors inside a less precision storage)
+ * should be done by invoking the `encode` and `decode` methods.
+ * - Changing alpha association should be done by invoking `premultiply_alpha` or
+ * `unpremultiply_alpha` methods.
+ *
+ * # Encoding.
+ *
+ * Color encoding is used to store colors with less precision as in using `uint8_t` in
+ * stead of `float`. This encoding is supported for `eSpace::SceneLinear`.
+ * To make this clear to the developer the `eSpace::SceneLinearByteEncoded`
+ * space is added.
+ *
+ * # Precision
+ *
+ * Colors can be stored using `uint8_t` or `float` colors. The conversion
+ * between the two precisions are available as methods. (`to_4b` and
+ * `to_4f`).
+ *
+ * # Alpha conversion
+ *
+ * Alpha conversion is only supported in SceneLinear space.
+ *
+ * Extending this file:
+ * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations
+ * of rgb based colors. `ColorHsl4f<eSpace::SceneLinear, eAlpha::Premultiplied>`
+ * - Add non RGB spaces/storages ColorXyz.
+ */
+
+/* Enumeration containing the different alpha modes. */
+enum class eAlpha {
+ /* Color and alpha are unassociated. */
+ Straight,
+ /* Color and alpha are associated. */
+ Premultiplied,
+};
+std::ostream &operator<<(std::ostream &stream, const eAlpha &space);
- Color4f() = default;
+/* Enumeration containing internal spaces. */
+enum class eSpace {
+ /* Blender theme color space (sRGB). */
+ Theme,
+ /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */
+ SceneLinear,
+ /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */
+ SceneLinearByteEncoded,
+};
+std::ostream &operator<<(std::ostream &stream, const eSpace &space);
- Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3])
+/* Template class to store RGBA values with different precision, space and alpha association. */
+template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA {
+ public:
+ ChannelStorageType r, g, b, a;
+ constexpr ColorRGBA() = default;
+
+ constexpr ColorRGBA(const ChannelStorageType rgba[4])
+ : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3])
{
}
- Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a)
+ constexpr ColorRGBA(const ChannelStorageType r,
+ const ChannelStorageType g,
+ const ChannelStorageType b,
+ const ChannelStorageType a)
+ : r(r), g(g), b(b), a(a)
{
}
- operator float *()
+ operator ChannelStorageType *()
{
return &r;
}
- operator const float *() const
+ operator const ChannelStorageType *() const
{
return &r;
}
- friend std::ostream &operator<<(std::ostream &stream, Color4f c)
+ friend std::ostream &operator<<(std::ostream &stream,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &c)
{
- stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
+
+ stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
return stream;
}
- friend bool operator==(const Color4f &a, const Color4f &b)
+ friend bool operator==(const ColorRGBA<ChannelStorageType, Space, Alpha> &a,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &b)
{
return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
}
- friend bool operator!=(const Color4f &a, const Color4f &b)
+ friend bool operator!=(const ColorRGBA<ChannelStorageType, Space, Alpha> &a,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &b)
{
return !(a == b);
}
@@ -71,58 +152,209 @@ struct Color4f {
}
};
-struct Color4b {
- uint8_t r, g, b, a;
+/* Forward declarations of concrete color classes. */
+template<eAlpha Alpha> class ColorSceneLinear4f;
+template<eAlpha Alpha> class ColorSceneLinearByteEncoded4b;
+template<typename ChannelStorageType> class ColorTheme4;
- Color4b() = default;
+/* Forward declaration of precision conversion methods. */
+BLI_INLINE ColorTheme4<float> BLI_color_convert_to_theme4f(const ColorTheme4<uint8_t> &srgb4b);
+BLI_INLINE ColorTheme4<uint8_t> BLI_color_convert_to_theme4b(const ColorTheme4<float> &srgb4f);
- Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a)
+template<eAlpha Alpha>
+class ColorSceneLinear4f final : public ColorRGBA<float, eSpace::SceneLinear, Alpha> {
+ public:
+ constexpr ColorSceneLinear4f<Alpha>() : ColorRGBA<float, eSpace::SceneLinear, Alpha>()
{
}
- Color4b(Color4f other)
+ constexpr ColorSceneLinear4f<Alpha>(const float *rgba)
+ : ColorRGBA<float, eSpace::SceneLinear, Alpha>(rgba)
{
- rgba_float_to_uchar(*this, other);
}
- operator Color4f() const
+ constexpr ColorSceneLinear4f<Alpha>(float r, float g, float b, float a)
+ : ColorRGBA<float, eSpace::SceneLinear, Alpha>(r, g, b, a)
{
- Color4f result;
- rgba_uchar_to_float(result, *this);
- return result;
}
- operator uint8_t *()
+ /**
+ * Convert to its byte encoded counter space.
+ **/
+ ColorSceneLinearByteEncoded4b<Alpha> encode() const
{
- return &r;
+ ColorSceneLinearByteEncoded4b<Alpha> encoded;
+ linearrgb_to_srgb_uchar4(encoded, *this);
+ return encoded;
}
- operator const uint8_t *() const
+ /**
+ * Convert color and alpha association to premultiplied alpha.
+ *
+ * Does nothing when color has already a premultiplied alpha.
+ */
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiply_alpha() const
{
- return &r;
+ if constexpr (Alpha == eAlpha::Straight) {
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied;
+ straight_to_premul_v4_v4(premultiplied, *this);
+ return premultiplied;
+ }
+ else {
+ return *this;
+ }
}
- friend std::ostream &operator<<(std::ostream &stream, Color4b c)
+ /**
+ * Convert color and alpha association to straight alpha.
+ *
+ * Does nothing when color has straighten alpha.
+ */
+ ColorSceneLinear4f<eAlpha::Straight> unpremultiply_alpha() const
{
- stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
- return stream;
+ if constexpr (Alpha == eAlpha::Premultiplied) {
+ ColorSceneLinear4f<eAlpha::Straight> straighten;
+ premul_to_straight_v4_v4(straighten, *this);
+ return straighten;
+ }
+ else {
+ return *this;
+ }
}
+};
- friend bool operator==(const Color4b &a, const Color4b &b)
+template<eAlpha Alpha>
+class ColorSceneLinearByteEncoded4b final
+ : public ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha> {
+ public:
+ constexpr ColorSceneLinearByteEncoded4b() = default;
+
+ constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba)
+ : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(rgba)
{
- return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
}
- friend bool operator!=(const Color4b &a, const Color4b &b)
+ constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+ : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(r, g, b, a)
{
- return !(a == b);
}
- uint64_t hash() const
+ /**
+ * Convert to back to float color.
+ */
+ ColorSceneLinear4f<Alpha> decode() const
+ {
+ ColorSceneLinear4f<Alpha> decoded;
+ srgb_to_linearrgb_uchar4(decoded, *this);
+ return decoded;
+ }
+};
+
+/**
+ * Theme color template class.
+ *
+ * Don't use directly, but use `ColorTheme4b/ColorTheme4b`.
+ *
+ * This has been implemented as a template to improve inlining. When implemented as concrete
+ * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be
+ * inlined.
+ */
+template<typename ChannelStorageType>
+class ColorTheme4 final : public ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight> {
+ public:
+ constexpr ColorTheme4() : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(){};
+
+ constexpr ColorTheme4(const ChannelStorageType *rgba)
+ : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(rgba)
+ {
+ }
+
+ constexpr ColorTheme4(ChannelStorageType r,
+ ChannelStorageType g,
+ ChannelStorageType b,
+ ChannelStorageType a)
+ : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(r, g, b, a)
+ {
+ }
+
+ /**
+ * Change precision of color to float.
+ */
+ ColorTheme4<float> to_4f() const
{
- return static_cast<uint64_t>(r * 1283591) ^ static_cast<uint64_t>(g * 850177) ^
- static_cast<uint64_t>(b * 735391) ^ static_cast<uint64_t>(a * 442319);
+ if constexpr ((std::is_same_v<ChannelStorageType, uint8_t>)) {
+ return BLI_color_convert_to_theme4f(*this);
+ }
+ else {
+ return *this;
+ }
+ }
+
+ /**
+ * Change precision of color to uint8_t.
+ */
+ ColorTheme4<uint8_t> to_4b() const
+ {
+ if constexpr ((std::is_same_v<ChannelStorageType, float>)) {
+ return BLI_color_convert_to_theme4b(*this);
+ }
+ else {
+ return *this;
+ }
}
};
+using ColorTheme4b = ColorTheme4<uint8_t>;
+using ColorTheme4f = ColorTheme4<float>;
+
+BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f)
+{
+ ColorTheme4b theme4b;
+ rgba_float_to_uchar(theme4b, theme4f);
+ return theme4b;
+}
+
+BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b)
+{
+ ColorTheme4f theme4f;
+ rgba_uchar_to_float(theme4f, theme4b);
+ return theme4f;
+}
+
+BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear(
+ const ColorTheme4f &theme4f)
+{
+ ColorSceneLinear4f<eAlpha::Straight> scene_linear;
+ srgb_to_linearrgb_v4(scene_linear, theme4f);
+ return scene_linear;
+}
+
+BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear(
+ const ColorTheme4b &theme4b)
+{
+ ColorSceneLinear4f<eAlpha::Straight> scene_linear;
+ srgb_to_linearrgb_uchar4(scene_linear, theme4b);
+ return scene_linear;
+}
+
+BLI_INLINE ColorTheme4f
+BLI_color_convert_to_theme4f(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear)
+{
+ ColorTheme4f theme4f;
+ linearrgb_to_srgb_v4(theme4f, scene_linear);
+ return theme4f;
+}
+
+BLI_INLINE ColorTheme4b
+BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear)
+{
+ ColorTheme4b theme4b;
+ linearrgb_to_srgb_uchar4(theme4b, scene_linear);
+ return theme4b;
+}
+
+/* Internal roles. For convenience to shorten the type names and hide complexity. */
+using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>;
+using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>;
+
} // namespace blender
diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh
index 48b01edcd6f..f04c0e9c80a 100644
--- a/source/blender/blenlib/BLI_index_mask.hh
+++ b/source/blender/blenlib/BLI_index_mask.hh
@@ -110,7 +110,7 @@ class IndexMask {
}
/**
- * Returns the n-th index referenced by this IndexMask. The `index_mask` method returns an
+ * Returns the n-th index referenced by this IndexMask. The `index_range` method returns an
* IndexRange containing all indices that can be used as parameter here.
*/
int64_t operator[](int64_t n) const
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index 028ca31a059..46219ad5493 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -154,6 +154,9 @@ MINLINE int max_iii(int a, int b, int c);
MINLINE int min_iiii(int a, int b, int c, int d);
MINLINE int max_iiii(int a, int b, int c, int d);
+MINLINE uint min_uu(uint a, uint b);
+MINLINE uint max_uu(uint a, uint b);
+
MINLINE size_t min_zz(size_t a, size_t b);
MINLINE size_t max_zz(size_t a, size_t b);
diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh
index fb5e2b61cdb..b9eda2ad7e1 100644
--- a/source/blender/blenlib/BLI_mpq3.hh
+++ b/source/blender/blenlib/BLI_mpq3.hh
@@ -218,6 +218,15 @@ struct mpq3 {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
+ static mpq_class dot_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer)
+ {
+ buffer = a;
+ buffer *= b;
+ buffer.x += buffer.y;
+ buffer.x += buffer.z;
+ return buffer.x;
+ }
+
static mpq3 cross(const mpq3 &a, const mpq3 &b)
{
return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
@@ -246,6 +255,13 @@ struct mpq3 {
return mpq3::dot(diff, diff);
}
+ static mpq_class distance_squared_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer)
+ {
+ buffer = a;
+ buffer -= b;
+ return mpq3::dot(buffer, buffer);
+ }
+
static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t)
{
mpq_class s = 1 - t;
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 8bea2584ca7..f6f546c90b9 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -161,7 +161,7 @@ class Vector {
}
/**
- * Create a vector from an array ref. The values in the vector are copy constructed.
+ * Create a vector from a span. The values in the vector are copy constructed.
*/
template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
Vector(Span<U> values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator)
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index ce3515ac153..f3dc343ee20 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -39,6 +39,7 @@ set(SRC
intern/BLI_args.c
intern/BLI_array.c
intern/BLI_assert.c
+ intern/BLI_color.cc
intern/BLI_dial_2d.c
intern/BLI_dynstr.c
intern/BLI_filelist.c
@@ -389,6 +390,7 @@ if(WITH_GTESTS)
tests/BLI_array_store_test.cc
tests/BLI_array_test.cc
tests/BLI_array_utils_test.cc
+ tests/BLI_color_test.cc
tests/BLI_delaunay_2d_test.cc
tests/BLI_disjoint_set_test.cc
tests/BLI_edgehash_test.cc
diff --git a/source/blender/blenlib/intern/BLI_color.cc b/source/blender/blenlib/intern/BLI_color.cc
new file mode 100644
index 00000000000..6dcef4f4688
--- /dev/null
+++ b/source/blender/blenlib/intern/BLI_color.cc
@@ -0,0 +1,55 @@
+/*
+ * 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 "BLI_color.hh"
+
+namespace blender {
+
+std::ostream &operator<<(std::ostream &stream, const eAlpha &space)
+{
+ switch (space) {
+ case eAlpha::Straight: {
+ stream << "Straight";
+ break;
+ }
+ case eAlpha::Premultiplied: {
+ stream << "Premultiplied";
+ break;
+ }
+ }
+ return stream;
+}
+
+std::ostream &operator<<(std::ostream &stream, const eSpace &space)
+{
+ switch (space) {
+ case eSpace::Theme: {
+ stream << "Theme";
+ break;
+ }
+ case eSpace::SceneLinear: {
+ stream << "SceneLinear";
+ break;
+ }
+ case eSpace::SceneLinearByteEncoded: {
+ stream << "SceneLinearByteEncoded";
+ break;
+ }
+ }
+ return stream;
+}
+
+} // namespace blender
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index 106bd5bc793..107c27da6a2 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -171,7 +171,7 @@ size_t BLI_gzip_mem_to_file_at_pos(
z_stream strm;
unsigned char out[CHUNK];
- fseek(file, gz_stream_offset, 0);
+ BLI_fseek(file, gz_stream_offset, 0);
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
@@ -217,7 +217,7 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g
size_t chunk = 256 * 1024;
unsigned char in[CHUNK];
- fseek(file, gz_stream_offset, 0);
+ BLI_fseek(file, gz_stream_offset, 0);
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 2a7c091d1b9..d73afff64c8 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -514,6 +514,15 @@ MINLINE int max_ii(int a, int b)
return (b < a) ? a : b;
}
+MINLINE uint min_uu(uint a, uint b)
+{
+ return (a < b) ? a : b;
+}
+MINLINE uint max_uu(uint a, uint b)
+{
+ return (b < a) ? a : b;
+}
+
MINLINE float min_fff(float a, float b, float c)
{
return min_ff(min_ff(a, b), c);
@@ -798,9 +807,9 @@ MINLINE unsigned char unit_float_to_uchar_clamp(float val)
MINLINE unsigned short unit_float_to_ushort_clamp(float val)
{
- return (unsigned short)((val >= 1.0f - 0.5f / 65535) ?
- 65535 :
- (val <= 0.0f) ? 0 : (val * 65535.0f + 0.5f));
+ return (unsigned short)((val >= 1.0f - 0.5f / 65535) ? 65535 :
+ (val <= 0.0f) ? 0 :
+ (val * 65535.0f + 0.5f));
}
#define unit_float_to_ushort_clamp(val) \
((CHECK_TYPE_INLINE(val, float)), unit_float_to_ushort_clamp(val))
diff --git a/source/blender/blenlib/intern/memory_utils.c b/source/blender/blenlib/intern/memory_utils.c
index d477c20a242..5ca7b96c136 100644
--- a/source/blender/blenlib/intern/memory_utils.c
+++ b/source/blender/blenlib/intern/memory_utils.c
@@ -31,7 +31,7 @@
#include "BLI_strict_flags.h"
/**
- * Check if memory is zero'd, as with memset(arr, 0, arr_size)
+ * Check if memory is zeroed, as with `memset(arr, 0, arr_size)`.
*/
bool BLI_memory_is_zero(const void *arr, const size_t arr_size)
{
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index 25291b8c3b0..00d53a010b0 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -41,6 +41,7 @@
# include "BLI_set.hh"
# include "BLI_span.hh"
# include "BLI_stack.hh"
+# include "BLI_task.hh"
# include "BLI_vector.hh"
# include "BLI_vector_set.hh"
@@ -48,6 +49,10 @@
# include "BLI_mesh_boolean.hh"
+# ifdef WITH_TBB
+# include "tbb/spin_mutex.h"
+# endif
+
// # define PERFDEBUG
namespace blender::meshintersect {
@@ -144,11 +149,9 @@ class TriMeshTopology : NonCopyable {
* Else return NO_INDEX. */
int other_tri_if_manifold(Edge e, int t) const
{
- if (edge_tri_.contains(e)) {
- auto *p = edge_tri_.lookup(e);
- if (p->size() == 2) {
- return ((*p)[0] == t) ? (*p)[1] : (*p)[0];
- }
+ auto p = edge_tri_.lookup_ptr(e);
+ if (p != nullptr && (*p)->size() == 2) {
+ return ((**p)[0] == t) ? (**p)[1] : (**p)[0];
}
return NO_INDEX;
}
@@ -1690,9 +1693,24 @@ static int find_containing_cell(const Vert *v,
* If the closest point is on an edge, return 0, 1, or 2
* for edges ab, bc, or ca in *r_edge; else -1.
* (Adapted from #closest_on_tri_to_point_v3()).
+ * The arguments ab, ac, ..., r are used as temporaries
+ * in this routine. Passing them in from the caller can
+ * avoid many allocs and frees of temporary mpq3 values
+ * and the mpq_class values within them.
*/
-static mpq_class closest_on_tri_to_point(
- const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert)
+static mpq_class closest_on_tri_to_point(const mpq3 &p,
+ const mpq3 &a,
+ const mpq3 &b,
+ const mpq3 &c,
+ mpq3 &ab,
+ mpq3 &ac,
+ mpq3 &ap,
+ mpq3 &bp,
+ mpq3 &cp,
+ mpq3 &m,
+ mpq3 &r,
+ int *r_edge,
+ int *r_vert)
{
constexpr int dbg_level = 0;
if (dbg_level > 0) {
@@ -1700,11 +1718,15 @@ static mpq_class closest_on_tri_to_point(
std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n";
}
/* Check if p in vertex region outside a. */
- mpq3 ab = b - a;
- mpq3 ac = c - a;
- mpq3 ap = p - a;
- mpq_class d1 = mpq3::dot(ab, ap);
- mpq_class d2 = mpq3::dot(ac, ap);
+ ab = b;
+ ab -= a;
+ ac = c;
+ ac -= a;
+ ap = p;
+ ap -= a;
+
+ mpq_class d1 = mpq3::dot_with_buffer(ab, ap, m);
+ mpq_class d2 = mpq3::dot_with_buffer(ac, ap, m);
if (d1 <= 0 && d2 <= 0) {
/* Barycentric coordinates (1,0,0). */
*r_edge = -1;
@@ -1712,12 +1734,13 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = a\n";
}
- return mpq3::distance_squared(p, a);
+ return mpq3::distance_squared_with_buffer(p, a, m);
}
/* Check if p in vertex region outside b. */
- mpq3 bp = p - b;
- mpq_class d3 = mpq3::dot(ab, bp);
- mpq_class d4 = mpq3::dot(ac, bp);
+ bp = p;
+ bp -= b;
+ mpq_class d3 = mpq3::dot_with_buffer(ab, bp, m);
+ mpq_class d4 = mpq3::dot_with_buffer(ac, bp, m);
if (d3 >= 0 && d4 <= d3) {
/* Barycentric coordinates (0,1,0). */
*r_edge = -1;
@@ -1725,25 +1748,28 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = b\n";
}
- return mpq3::distance_squared(p, b);
+ return mpq3::distance_squared_with_buffer(p, b, m);
}
/* Check if p in region of ab. */
mpq_class vc = d1 * d4 - d3 * d2;
if (vc <= 0 && d1 >= 0 && d3 <= 0) {
mpq_class v = d1 / (d1 - d3);
/* Barycentric coordinates (1-v,v,0). */
- mpq3 r = a + v * ab;
+ r = ab;
+ r *= v;
+ r += a;
*r_vert = -1;
*r_edge = 0;
if (dbg_level > 0) {
std::cout << " answer = on ab at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* Check if p in vertex region outside c. */
- mpq3 cp = p - c;
- mpq_class d5 = mpq3::dot(ab, cp);
- mpq_class d6 = mpq3::dot(ac, cp);
+ cp = p;
+ cp -= c;
+ mpq_class d5 = mpq3::dot_with_buffer(ab, cp, m);
+ mpq_class d6 = mpq3::dot_with_buffer(ac, cp, m);
if (d6 >= 0 && d5 <= d6) {
/* Barycentric coordinates (0,0,1). */
*r_edge = -1;
@@ -1751,49 +1777,67 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = c\n";
}
- return mpq3::distance_squared(p, c);
+ return mpq3::distance_squared_with_buffer(p, c, m);
}
/* Check if p in edge region of ac. */
mpq_class vb = d5 * d2 - d1 * d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
mpq_class w = d2 / (d2 - d6);
/* Barycentric coordinates (1-w,0,w). */
- mpq3 r = a + w * ac;
+ r = ac;
+ r *= w;
+ r += a;
*r_vert = -1;
*r_edge = 2;
if (dbg_level > 0) {
std::cout << " answer = on ac at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* Check if p in edge region of bc. */
mpq_class va = d3 * d6 - d5 * d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
/* Barycentric coordinates (0,1-w,w). */
- mpq3 r = c - b;
- r = w * r;
- r = r + b;
+ r = c;
+ r -= b;
+ r *= w;
+ r += b;
*r_vert = -1;
*r_edge = 1;
if (dbg_level > 0) {
std::cout << " answer = on bc at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* p inside face region. Compute barycentric coordinates (u,v,w). */
mpq_class denom = 1 / (va + vb + vc);
mpq_class v = vb * denom;
mpq_class w = vc * denom;
- ac = w * ac;
- mpq3 r = a + v * ab;
- r = r + ac;
+ ac *= w;
+ r = ab;
+ r *= v;
+ r += a;
+ r += ac;
*r_vert = -1;
*r_edge = -1;
if (dbg_level > 0) {
std::cout << " answer = inside at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
+}
+
+static float closest_on_tri_to_point_float_dist_squared(const float3 &p,
+ const double3 &a,
+ const double3 &b,
+ const double3 &c)
+{
+ float3 fa, fb, fc, closest;
+ copy_v3fl_v3db(fa, a);
+ copy_v3fl_v3db(fb, b);
+ copy_v3fl_v3db(fc, c);
+ closest_on_tri_to_point_v3(closest, p, fa, fb, fc);
+ return len_squared_v3v3(p, closest);
}
struct ComponentContainer {
@@ -1832,6 +1876,11 @@ static Vector<ComponentContainer> find_component_containers(int comp,
if (dbg_level > 0) {
std::cout << "test vertex in comp: " << test_v << "\n";
}
+ const double3 &test_v_d = test_v->co;
+ float3 test_v_f(test_v_d[0], test_v_d[1], test_v_d[2]);
+
+ mpq3 buf[7];
+
for (int comp_other : components.index_range()) {
if (comp == comp_other) {
continue;
@@ -1843,6 +1892,7 @@ static Vector<ComponentContainer> find_component_containers(int comp,
int nearest_tri_close_vert = -1;
int nearest_tri_close_edge = -1;
mpq_class nearest_tri_dist_squared;
+ float nearest_tri_dist_squared_float = FLT_MAX;
for (int p : components[comp_other]) {
const Patch &patch = pinfo.patch(p);
for (int t : patch.tris()) {
@@ -1852,10 +1902,23 @@ static Vector<ComponentContainer> find_component_containers(int comp,
}
int close_vert;
int close_edge;
+ /* Try a cheap float test first. */
+ float d2_f = closest_on_tri_to_point_float_dist_squared(
+ test_v_f, tri[0]->co, tri[1]->co, tri[2]->co);
+ if (d2_f - FLT_EPSILON > nearest_tri_dist_squared_float) {
+ continue;
+ }
mpq_class d2 = closest_on_tri_to_point(test_v->co_exact,
tri[0]->co_exact,
tri[1]->co_exact,
tri[2]->co_exact,
+ buf[0],
+ buf[1],
+ buf[2],
+ buf[3],
+ buf[4],
+ buf[5],
+ buf[6],
&close_edge,
&close_vert);
if (dbg_level > 1) {
@@ -1867,6 +1930,7 @@ static Vector<ComponentContainer> find_component_containers(int comp,
nearest_tri_close_edge = close_edge;
nearest_tri_close_vert = close_vert;
nearest_tri_dist_squared = d2;
+ nearest_tri_dist_squared_float = d2_f;
}
}
}
@@ -2567,47 +2631,58 @@ static IMesh raycast_tris_boolean(const IMesh &tm,
BVHTree *tree = raycast_tree(tm);
Vector<Face *> out_faces;
out_faces.reserve(tm.face_size());
- Array<float> in_shape(nshapes, 0);
- Array<int> winding(nshapes, 0);
- for (int t : tm.face_index_range()) {
- Face &tri = *tm.face(t);
- int shape = shape_fn(tri.orig);
- if (dbg_level > 0) {
- std::cout << "process triangle " << t << " = " << &tri << "\n";
- std::cout << "shape = " << shape << "\n";
- }
- test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape);
- for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
- if (other_shape == shape) {
- continue;
- }
- /* The in_shape array has a confidence value for "insideness".
- * For most operations, even a hint of being inside
- * gives good results, but when shape is a cutter in a Difference
- * operation, we want to be pretty sure that the point is inside other_shape.
- * E.g., T75827.
- * Also, when the operation is intersection, we also want high confidence.
- */
- bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
- op == BoolOpType::Intersect;
- bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
+# ifdef WITH_TBB
+ tbb::spin_mutex mtx;
+# endif
+ const int grainsize = 256;
+ parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) {
+ Array<float> in_shape(nshapes, 0);
+ Array<int> winding(nshapes, 0);
+ for (int t : range) {
+ Face &tri = *tm.face(t);
+ int shape = shape_fn(tri.orig);
if (dbg_level > 0) {
- std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
- << other_shape << " val = " << in_shape[other_shape] << "\n";
+ std::cout << "process triangle " << t << " = " << &tri << "\n";
+ std::cout << "shape = " << shape << "\n";
}
- winding[other_shape] = inside;
- }
- bool do_flip;
- bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
- if (!do_remove) {
- if (!do_flip) {
- out_faces.append(&tri);
+ test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape);
+ for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
+ if (other_shape == shape) {
+ continue;
+ }
+ /* The in_shape array has a confidence value for "insideness".
+ * For most operations, even a hint of being inside
+ * gives good results, but when shape is a cutter in a Difference
+ * operation, we want to be pretty sure that the point is inside other_shape.
+ * E.g., T75827.
+ * Also, when the operation is intersection, we also want high confidence.
+ */
+ bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
+ op == BoolOpType::Intersect;
+ bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
+ if (dbg_level > 0) {
+ std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
+ << other_shape << " val = " << in_shape[other_shape] << "\n";
+ }
+ winding[other_shape] = inside;
}
- else {
- raycast_add_flipped(out_faces, tri, arena);
+ bool do_flip;
+ bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
+ {
+# ifdef WITH_TBB
+ tbb::spin_mutex::scoped_lock lock(mtx);
+# endif
+ if (!do_remove) {
+ if (!do_flip) {
+ out_faces.append(&tri);
+ }
+ else {
+ raycast_add_flipped(out_faces, tri, arena);
+ }
+ }
}
}
- }
+ });
BLI_bvhtree_free(tree);
ans.set_faces(out_faces);
return ans;
@@ -3259,9 +3334,13 @@ static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out,
std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n";
}
/* For now: need plane normals for all triangles. */
- for (Face *tri : tm_out.faces()) {
- tri->populate_plane(false);
- }
+ const int grainsize = 1024;
+ parallel_for(tm_out.face_index_range(), grainsize, [&](IndexRange range) {
+ for (int i : range) {
+ Face *tri = tm_out.face(i);
+ tri->populate_plane(false);
+ }
+ });
/* Gather all output triangles that are part of each input face.
* face_output_tris[f] will be indices of triangles in tm_out
* that have f as their original face. */
diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc
index 636209883c3..97f856476c5 100644
--- a/source/blender/blenlib/intern/mesh_intersect.cc
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -1165,13 +1165,19 @@ static int filter_plane_side(const double3 &p,
* Assumes ab is not perpendicular to n.
* This works because the ratio of the projections of ab and ac onto n is the same as
* the ratio along the line ab of the intersection point to the whole of ab.
+ * The ab, ac, and dotbuf arguments are used as a temporaries; declaring them
+ * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures.
*/
-static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n)
-{
- mpq3 ab = a - b;
- mpq_class den = mpq3::dot(ab, n);
+static inline mpq3 tti_interp(
+ const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf)
+{
+ ab = a;
+ ab -= b;
+ ac = a;
+ ac -= c;
+ mpq_class den = mpq3::dot_with_buffer(ab, n, dotbuf);
BLI_assert(den != 0);
- mpq_class alpha = mpq3::dot(a - c, n) / den;
+ mpq_class alpha = mpq3::dot_with_buffer(ac, n, dotbuf) / den;
return a - alpha * ab;
}
@@ -1179,11 +1185,28 @@ static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const
* Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW
* order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations.
* TODO: change arguments to `const Vert *` and use floating filters.
+ * The ba, ca, n, and dotbuf arguments are used as temporaries; declaring them
+ * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures.
*/
-static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad)
+static inline int tti_above(const mpq3 &a,
+ const mpq3 &b,
+ const mpq3 &c,
+ const mpq3 &ad,
+ mpq3 &ba,
+ mpq3 &ca,
+ mpq3 &n,
+ mpq3 &dotbuf)
{
- mpq3 n = mpq3::cross(b - a, c - a);
- return sgn(mpq3::dot(ad, n));
+ ba = b;
+ ba -= a;
+ ca = c;
+ ca -= a;
+
+ n.x = ba.y * ca.z - ba.z * ca.y;
+ n.y = ba.z * ca.x - ba.x * ca.z;
+ n.z = ba.x * ca.y - ba.y * ca.x;
+
+ return sgn(mpq3::dot_with_buffer(ad, n, dotbuf));
}
/**
@@ -1227,20 +1250,21 @@ static ITT_value itt_canon2(const mpq3 &p1,
mpq3 p1p2 = p2 - p1;
mpq3 intersect_1;
mpq3 intersect_2;
+ mpq3 buf[4];
bool no_overlap = false;
/* Top test in classification tree. */
- if (tti_above(p1, q1, r2, p1p2) > 0) {
+ if (tti_above(p1, q1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) {
/* Middle right test in classification tree. */
- if (tti_above(p1, r1, r2, p1p2) <= 0) {
+ if (tti_above(p1, r1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) <= 0) {
/* Bottom right test in classification tree. */
- if (tti_above(p1, r1, q2, p1p2) > 0) {
+ if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) {
/* Overlap is [k [i l] j]. */
if (dbg_level > 0) {
std::cout << "overlap [k [i l] j]\n";
}
/* i is intersect with p1r1. l is intersect with p2r2. */
- intersect_1 = tti_interp(p1, r1, p2, n2);
- intersect_2 = tti_interp(p2, r2, p1, n1);
+ intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]);
}
else {
/* Overlap is [i [k l] j]. */
@@ -1248,8 +1272,8 @@ static ITT_value itt_canon2(const mpq3 &p1,
std::cout << "overlap [i [k l] j]\n";
}
/* k is intersect with p2q2. l is intersect is p2r2. */
- intersect_1 = tti_interp(p2, q2, p1, n1);
- intersect_2 = tti_interp(p2, r2, p1, n1);
+ intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]);
}
}
else {
@@ -1262,7 +1286,7 @@ static ITT_value itt_canon2(const mpq3 &p1,
}
else {
/* Middle left test in classification tree. */
- if (tti_above(p1, q1, q2, p1p2) < 0) {
+ if (tti_above(p1, q1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) < 0) {
/* No overlap: [i j] [k l]. */
if (dbg_level > 0) {
std::cout << "no overlap: [i j] [k l]\n";
@@ -1271,14 +1295,14 @@ static ITT_value itt_canon2(const mpq3 &p1,
}
else {
/* Bottom left test in classification tree. */
- if (tti_above(p1, r1, q2, p1p2) >= 0) {
+ if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) >= 0) {
/* Overlap is [k [i j] l]. */
if (dbg_level > 0) {
std::cout << "overlap [k [i j] l]\n";
}
/* i is intersect with p1r1. j is intersect with p1q1. */
- intersect_1 = tti_interp(p1, r1, p2, n2);
- intersect_2 = tti_interp(p1, q1, p2, n2);
+ intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]);
}
else {
/* Overlap is [i [k j] l]. */
@@ -1286,8 +1310,8 @@ static ITT_value itt_canon2(const mpq3 &p1,
std::cout << "overlap [i [k j] l]\n";
}
/* k is intersect with p2q2. j is intersect with p1q1. */
- intersect_1 = tti_interp(p2, q2, p1, n1);
- intersect_2 = tti_interp(p1, q1, p2, n2);
+ intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]);
}
}
}
@@ -1438,6 +1462,7 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
return ITT_value(INONE);
}
+ mpq3 buf[2];
const mpq3 &p1 = vp1->co_exact;
const mpq3 &q1 = vq1->co_exact;
const mpq3 &r1 = vr1->co_exact;
@@ -1447,13 +1472,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
const mpq3 &n2 = tri2.plane->norm_exact;
if (sp1 == 0) {
- sp1 = sgn(mpq3::dot(p1 - r2, n2));
+ buf[0] = p1;
+ buf[0] -= r2;
+ sp1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (sq1 == 0) {
- sq1 = sgn(mpq3::dot(q1 - r2, n2));
+ buf[0] = q1;
+ buf[0] -= r2;
+ sq1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (sr1 == 0) {
- sr1 = sgn(mpq3::dot(r1 - r2, n2));
+ buf[0] = r1;
+ buf[0] -= r2;
+ sr1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (dbg_level > 1) {
@@ -1473,13 +1504,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
/* Repeat for signs of t2's vertices with respect to plane of t1. */
const mpq3 &n1 = tri1.plane->norm_exact;
if (sp2 == 0) {
- sp2 = sgn(mpq3::dot(p2 - r1, n1));
+ buf[0] = p2;
+ buf[0] -= r1;
+ sp2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (sq2 == 0) {
- sq2 = sgn(mpq3::dot(q2 - r1, n1));
+ buf[0] = q2;
+ buf[0] -= r1;
+ sq2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (sr2 == 0) {
- sr2 = sgn(mpq3::dot(r2 - r1, n1));
+ buf[0] = r2;
+ buf[0] -= r1;
+ sr2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (dbg_level > 1) {
diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c
index 7bfca149ffb..98fa5c872b0 100644
--- a/source/blender/blenlib/intern/polyfill_2d_beautify.c
+++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c
@@ -375,7 +375,7 @@ void BLI_polyfill_beautify(const float (*coords)[2],
for (uint i = 0, base_index = 0; i < order_edges_len; base_index++) {
const struct OrderEdge *oe_a = &order_edges[i++];
const struct OrderEdge *oe_b = &order_edges[i++];
- BLI_assert(oe_a->verts[0] == oe_a->verts[0] && oe_a->verts[1] == oe_a->verts[1]);
+ BLI_assert(oe_a->verts[0] == oe_b->verts[0] && oe_a->verts[1] == oe_b->verts[1]);
half_edges[oe_a->e_half].e_radial = oe_b->e_half;
half_edges[oe_b->e_half].e_radial = oe_a->e_half;
half_edges[oe_a->e_half].base_index = base_index;
diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c
index 9586da941a4..7d7436411ac 100644
--- a/source/blender/blenlib/intern/timecode.c
+++ b/source/blender/blenlib/intern/timecode.c
@@ -216,10 +216,10 @@ size_t BLI_timecode_string_from_time_simple(char *str,
const int hun = ((int)(fmod(time_seconds, 1.0) * 100));
if (hr) {
- rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
+ rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
}
else {
- rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun);
+ rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun);
}
return rlen;
diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c
index 333b6783087..3aa61d1fec5 100644
--- a/source/blender/blenlib/intern/winstuff.c
+++ b/source/blender/blenlib/intern/winstuff.c
@@ -94,9 +94,9 @@ void BLI_windows_register_blend_extension(const bool background)
GetModuleFileName(0, BlPath, MAX_PATH);
/* Replace the actual app name with the wrapper. */
- blender_app = strstr(BlPath, "blender-app.exe");
+ blender_app = strstr(BlPath, "blender.exe");
if (blender_app != NULL) {
- strcpy(blender_app, "blender.exe");
+ strcpy(blender_app, "blender-launcher.exe");
}
/* root is HKLM by default */
diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc
new file mode 100644
index 00000000000..14796e6bf71
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_color_test.cc
@@ -0,0 +1,133 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_color.hh"
+
+namespace blender::tests {
+
+/**
+ * \name Conversions
+ * \{ */
+
+TEST(color, ThemeByteToFloat)
+{
+ ColorTheme4b theme_byte(192, 128, 64, 128);
+ ColorTheme4f theme_float = theme_byte.to_4f();
+ EXPECT_NEAR(0.75f, theme_float.r, 0.01f);
+ EXPECT_NEAR(0.5f, theme_float.g, 0.01f);
+ EXPECT_NEAR(0.25f, theme_float.b, 0.01f);
+ EXPECT_NEAR(0.5f, theme_float.a, 0.01f);
+}
+
+TEST(color, SrgbStraightFloatToByte)
+{
+ ColorTheme4f theme_float(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme_byte = theme_float.to_4b();
+ EXPECT_EQ(191, theme_byte.r);
+ EXPECT_EQ(128, theme_byte.g);
+ EXPECT_EQ(64, theme_byte.b);
+ EXPECT_EQ(128, theme_byte.a);
+}
+
+TEST(color, SrgbStraightToSceneLinearPremultiplied)
+{
+ BLI_init_srgb_conversion();
+
+ ColorTheme4b theme(192, 128, 64, 128);
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear =
+ BLI_color_convert_to_scene_linear(theme).premultiply_alpha();
+ EXPECT_NEAR(0.26f, linear.r, 0.01f);
+ EXPECT_NEAR(0.11f, linear.g, 0.01f);
+ EXPECT_NEAR(0.02f, linear.b, 0.01f);
+ EXPECT_NEAR(0.5f, linear.a, 0.01f);
+}
+
+TEST(color, SceneLinearStraightToPremultiplied)
+{
+ ColorSceneLinear4f<eAlpha::Straight> straight(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied = straight.premultiply_alpha();
+ EXPECT_NEAR(0.37f, premultiplied.r, 0.01f);
+ EXPECT_NEAR(0.25f, premultiplied.g, 0.01f);
+ EXPECT_NEAR(0.12f, premultiplied.b, 0.01f);
+ EXPECT_NEAR(0.5f, premultiplied.a, 0.01f);
+}
+
+TEST(color, SceneLinearPremultipliedToStraight)
+{
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinear4f<eAlpha::Straight> straight = premultiplied.unpremultiply_alpha();
+ EXPECT_NEAR(1.5f, straight.r, 0.01f);
+ EXPECT_NEAR(1.0f, straight.g, 0.01f);
+ EXPECT_NEAR(0.5f, straight.b, 0.01f);
+ EXPECT_NEAR(0.5f, straight.a, 0.01f);
+}
+
+TEST(color, SceneLinearStraightSrgbFloat)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4f theme = BLI_color_convert_to_theme4f(linear);
+ EXPECT_NEAR(0.88f, theme.r, 0.01);
+ EXPECT_NEAR(0.73f, theme.g, 0.01);
+ EXPECT_NEAR(0.53f, theme.b, 0.01);
+ EXPECT_NEAR(0.5f, theme.a, 0.01);
+}
+
+TEST(color, SceneLinearPremultipliedToSrgbFloat)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4f theme = BLI_color_convert_to_theme4f(linear.unpremultiply_alpha());
+
+ EXPECT_NEAR(1.19f, theme.r, 0.01);
+ EXPECT_NEAR(1.0f, theme.g, 0.01);
+ EXPECT_NEAR(0.74f, theme.b, 0.01);
+ EXPECT_NEAR(0.5f, theme.a, 0.01);
+}
+
+TEST(color, SceneLinearStraightSrgbByte)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme = BLI_color_convert_to_theme4b(linear);
+ EXPECT_EQ(225, theme.r);
+ EXPECT_EQ(188, theme.g);
+ EXPECT_EQ(137, theme.b);
+ EXPECT_EQ(128, theme.a);
+}
+
+TEST(color, SceneLinearPremultipliedToSrgbByte)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme = BLI_color_convert_to_theme4b(linear.unpremultiply_alpha());
+ EXPECT_EQ(255, theme.r);
+ EXPECT_EQ(255, theme.g);
+ EXPECT_EQ(188, theme.b);
+ EXPECT_EQ(128, theme.a);
+}
+
+TEST(color, SceneLinearByteEncoding)
+{
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded = linear.encode();
+ EXPECT_EQ(225, encoded.r);
+ EXPECT_EQ(188, encoded.g);
+ EXPECT_EQ(137, encoded.b);
+ EXPECT_EQ(128, encoded.a);
+}
+
+TEST(color, SceneLinearByteDecoding)
+{
+ ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded(225, 188, 137, 128);
+ ColorSceneLinear4f<eAlpha::Premultiplied> decoded = encoded.decode();
+ EXPECT_NEAR(0.75f, decoded.r, 0.01f);
+ EXPECT_NEAR(0.5f, decoded.g, 0.01f);
+ EXPECT_NEAR(0.25f, decoded.b, 0.01f);
+ EXPECT_NEAR(0.5f, decoded.a, 0.01f);
+}
+
+/* \} */
+
+} // namespace blender::tests