diff options
Diffstat (limited to 'source/blender/blenlib')
-rw-r--r-- | source/blender/blenlib/BLI_color.hh | 2 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_color_mix.hh | 1051 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_generic_virtual_array.hh | 16 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_base.hh | 5 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_color.hh | 19 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_vec_types.hh | 8 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_virtual_array.hh | 6 | ||||
-rw-r--r-- | source/blender/blenlib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/blenlib/intern/fileops.c | 92 |
9 files changed, 1138 insertions, 62 deletions
diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index 0754221eb65..98fd7d0f15d 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -345,5 +345,7 @@ BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_l using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>; using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>; +using ColorPaint4f = ColorSceneLinear4f<eAlpha::Straight>; +using ColorPaint4b = ColorSceneLinearByteEncoded4b<eAlpha::Straight>; } // namespace blender diff --git a/source/blender/blenlib/BLI_color_mix.hh b/source/blender/blenlib/BLI_color_mix.hh new file mode 100644 index 00000000000..4989ddc609e --- /dev/null +++ b/source/blender/blenlib/BLI_color_mix.hh @@ -0,0 +1,1051 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup blenlib + * + * Contains color mixing utilities. + */ + +#include "BLI_color.hh" +#include "BLI_math_base.h" +#include "BLI_math_color.h" +#include "BLI_sys_types.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" + +#include <type_traits> + +namespace blender::color { + +struct ByteTraits { + using ValueType = uchar; + using BlendType = int; + + inline static const uchar range = 255; /* Zero-based maximum value. */ + inline static const float frange = 255.0f; /* Convenient floating-point version of range. */ + inline static const int cmpRange = 254; + inline static const int expandedRange = 256; /* One-based maximum value. */ + + inline static const int bytes = 1; + inline static const float unit = 255.0f; + + static inline BlendType divide_round(BlendType a, BlendType b) + { + return divide_round_i(a, b); + } + + static inline BlendType min(BlendType a, BlendType b) + { + return min_ii(a, b); + } + + static inline BlendType max(BlendType a, BlendType b) + { + return max_ii(a, b); + } + /* Discretizes in steps of 1.0 / range */ + static inline ValueType round(float f) + { + return round_fl_to_uchar(f); + } +}; + +struct FloatTraits { + using ValueType = float; + using BlendType = float; + + inline const static float range = 1.0f; + inline const static float frange = 1.0f; + inline const static float cmpRange = 0.9999f; + inline static const int expandedRange = 1.0f; + + inline const static float unit = 1.0f; + inline const static int bytes = 4; + + static inline BlendType divide_round(BlendType a, BlendType b) + { + return a / b; + } + + static inline BlendType min(BlendType a, BlendType b) + { + return min_ff(a, b); + } + + static inline BlendType max(BlendType a, BlendType b) + { + return min_ff(a, b); + } + + /* Discretizes in steps of 1.0 / range */ + static inline ValueType round(float f) + { + return f; + } +}; + +static float get_luminance(ColorPaint4f c) +{ + return IMB_colormanagement_get_luminance(&c.r); +} + +static int get_luminance(ColorPaint4b c) +{ + return IMB_colormanagement_get_luminance_byte(&c.r); +} + +#define EPS_SATURATION 0.0005f + +/* -------------------------------------------------------------------- */ +/** \name Color Blending Modes + * \{ */ + +template<typename Color, typename Traits> +static Color mix_blend(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Color col_mix(0, 0, 0, 0); + Blend mfac; + + if (fac == 0) { + return col_src; + } + + if (fac >= Traits::range) { + return col_dst; + } + + mfac = Traits::range - fac; + + cp_src = &col_src.r; + cp_dst = &col_dst.r; + cp_mix = &col_mix.r; + + /* Updated to use the rgb squared color model which blends nicer. */ + Blend r1 = cp_src[0] * cp_src[0]; + Blend g1 = cp_src[1] * cp_src[1]; + Blend b1 = cp_src[2] * cp_src[2]; + Blend a1 = cp_src[3] * cp_src[3]; + + Blend r2 = cp_dst[0] * cp_dst[0]; + Blend g2 = cp_dst[1] * cp_dst[1]; + Blend b2 = cp_dst[2] * cp_dst[2]; + Blend a2 = cp_dst[3] * cp_dst[3]; + + cp_mix[0] = Traits::round(sqrtf(Traits::divide_round((mfac * r1 + fac * r2), Traits::range))); + cp_mix[1] = Traits::round(sqrtf(Traits::divide_round((mfac * g1 + fac * g2), Traits::range))); + cp_mix[2] = Traits::round(sqrtf(Traits::divide_round((mfac * b1 + fac * b2), Traits::range))); + cp_mix[3] = Traits::round(sqrtf(Traits::divide_round((mfac * a1 + fac * a2), Traits::range))); + return Color(col_mix[0], col_mix[1], col_mix[2], col_mix[3]); + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_add(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + cp_src = (Value *)&col_src.r; + cp_dst = (Value *)&col_dst.r; + cp_mix = (Value *)&col_mix.r; + + temp = cp_src[0] + Traits::divide_round((fac * cp_dst[0]), Traits::range); + cp_mix[0] = (temp > Traits::cmpRange) ? Traits::range : temp; + temp = cp_src[1] + Traits::divide_round((fac * cp_dst[1]), Traits::range); + cp_mix[1] = (temp > Traits::cmpRange) ? Traits::range : temp; + temp = cp_src[2] + Traits::divide_round((fac * cp_dst[2]), Traits::range); + cp_mix[2] = (temp > Traits::cmpRange) ? Traits::range : temp; + temp = cp_src[3] + Traits::divide_round((fac * cp_dst[3]), Traits::range); + cp_mix[3] = (temp > Traits::cmpRange) ? Traits::range : temp; + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_sub(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend temp; + Color col_mix(0, 0, 0, 0); + + cp_src = (Value *)&col_src.r; + cp_dst = (Value *)&col_dst.r; + cp_mix = (Value *)&col_mix.r; + + temp = cp_src[0] - Traits::divide_round((fac * cp_dst[0]), Traits::range); + cp_mix[0] = (temp < 0) ? 0 : temp; + temp = cp_src[1] - Traits::divide_round((fac * cp_dst[1]), Traits::range); + cp_mix[1] = (temp < 0) ? 0 : temp; + temp = cp_src[2] - Traits::divide_round((fac * cp_dst[2]), Traits::range); + cp_mix[2] = (temp < 0) ? 0 : temp; + temp = cp_src[3] - Traits::divide_round((fac * cp_dst[3]), Traits::range); + cp_mix[3] = (temp < 0) ? 0 : temp; + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_mul(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + /* first mul, then blend the fac */ + cp_mix[0] = Traits::divide_round(mfac * cp_src[0] * Traits::range + fac * cp_dst[0] * cp_src[0], + Traits::range * Traits::range); + cp_mix[1] = Traits::divide_round(mfac * cp_src[1] * Traits::range + fac * cp_dst[1] * cp_src[1], + Traits::range * Traits::range); + cp_mix[2] = Traits::divide_round(mfac * cp_src[2] * Traits::range + fac * cp_dst[2] * cp_src[2], + Traits::range * Traits::range); + cp_mix[3] = Traits::divide_round(mfac * cp_src[3] * Traits::range + fac * cp_dst[3] * cp_src[3], + Traits::range * Traits::range); + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_lighten(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + if (fac >= Traits::range) { + return col_dst; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + /* See if we're lighter, if so mix, else don't do anything. + * if the paint color is darker then the original, then ignore */ + if (get_luminance(cp_src) > get_luminance(cp_dst)) { + return col_src; + } + + cp_mix[0] = Traits::divide_round(mfac * cp_src[0] + fac * cp_dst[0], Traits::range); + cp_mix[1] = Traits::divide_round(mfac * cp_src[1] + fac * cp_dst[1], Traits::range); + cp_mix[2] = Traits::divide_round(mfac * cp_src[2] + fac * cp_dst[2], Traits::range); + cp_mix[3] = Traits::divide_round(mfac * cp_src[3] + fac * cp_dst[3], Traits::range); + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_darken(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + if (fac >= Traits::range) { + return col_dst; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + /* See if we're darker, if so mix, else don't do anything. + * if the paint color is brighter then the original, then ignore */ + if (get_luminance(cp_src) < get_luminance(cp_dst)) { + return col_src; + } + + cp_mix[0] = Traits::divide_round((mfac * cp_src[0] + fac * cp_dst[0]), Traits::range); + cp_mix[1] = Traits::divide_round((mfac * cp_src[1] + fac * cp_dst[1]), Traits::range); + cp_mix[2] = Traits::divide_round((mfac * cp_src[2] + fac * cp_dst[2]), Traits::range); + cp_mix[3] = Traits::divide_round((mfac * cp_src[3] + fac * cp_dst[3]), Traits::range); + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_colordodge(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + Blend dodgefac = (Blend)((float)Traits::range * 0.885f); /* ~225/255 */ + + temp = (cp_dst[0] == Traits::range) ? + Traits::range : + Traits::min((cp_src[0] * dodgefac) / (Traits::range - cp_dst[0]), Traits::range); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range; + temp = (cp_dst[1] == Traits::range) ? + Traits::range : + Traits::min((cp_src[1] * dodgefac) / (Traits::range - cp_dst[1]), Traits::range); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range; + temp = (cp_dst[2] == Traits::range) ? + Traits::range : + Traits::min((cp_src[2] * dodgefac) / (Traits::range - cp_dst[2]), Traits::range); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range; + temp = (cp_dst[3] == Traits::range) ? + Traits::range : + Traits::min((cp_src[3] * dodgefac) / (Traits::range - cp_dst[3]), Traits::range); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_difference(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + temp = abs(cp_src[0] - cp_dst[0]); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range; + temp = abs(cp_src[1] - cp_dst[1]); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range; + temp = abs(cp_src[2] - cp_dst[2]); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range; + temp = abs(cp_src[3] - cp_dst[3]); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_screen(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + temp = Traits::max(Traits::range - (((Traits::range - cp_src[0]) * (Traits::range - cp_dst[0])) / + Traits::range), + 0); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range; + temp = Traits::max(Traits::range - (((Traits::range - cp_src[1]) * (Traits::range - cp_dst[1])) / + Traits::range), + 0); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range; + temp = Traits::max(Traits::range - (((Traits::range - cp_src[2]) * (Traits::range - cp_dst[2])) / + Traits::range), + 0); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range; + temp = Traits::max(Traits::range - (((Traits::range - cp_src[3]) * (Traits::range - cp_dst[3])) / + Traits::range), + 0); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_hardlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp_dst[i] > (Traits::range / 2)) { + temp = Traits::range - ((Traits::range - 2 * (cp_dst[i] - (Traits::range / 2))) * + (Traits::range - cp_src[i]) / Traits::range); + } + else { + temp = (2 * cp_dst[i] * cp_src[i]) / Traits::expandedRange; + } + cp_mix[i] = Traits::min((mfac * cp_src[i] + temp * fac) / Traits::range, Traits::range); + } + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_overlay(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp_src[i] > (Traits::range / 2)) { + temp = Traits::range - ((Traits::range - 2 * (cp_src[i] - (Traits::range / 2))) * + (Traits::range - cp_dst[i]) / Traits::range); + } + else { + temp = (2 * cp_dst[i] * cp_src[i]) / Traits::expandedRange; + } + cp_mix[i] = Traits::min((mfac * cp_src[i] + temp * fac) / Traits::range, Traits::range); + } + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_softlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + /* Use divide_round so we don't alter original byte equations. */ + const int add = Traits::divide_round(Traits::range, 4); + + for (int i = 0; i < 4; i++) { + if (cp_src[i] < (Traits::range / 2)) { + temp = ((2 * ((cp_dst[i] / 2) + add)) * cp_src[i]) / Traits::range; + } + else { + temp = Traits::range - (2 * (Traits::range - ((cp_dst[i] / 2) + add)) * + (Traits::range - cp_src[i]) / Traits::range); + } + cp_mix[i] = (temp * fac + cp_src[i] * mfac) / Traits::range; + } + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_exclusion(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + temp = (Traits::range / 2) - + ((2 * (cp_src[i] - (Traits::range / 2)) * (cp_dst[i] - (Traits::range / 2))) / + Traits::range); + cp_mix[i] = (temp * fac + cp_src[i] * mfac) / Traits::range; + } + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_luminosity(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / Traits::frange, + cp_src[1] / Traits::frange, + cp_src[2] / Traits::frange, + &h1, + &s1, + &v1); + rgb_to_hsv(cp_dst[0] / Traits::frange, + cp_dst[1] / Traits::frange, + cp_dst[2] / Traits::frange, + &h2, + &s2, + &v2); + + v1 = v2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range; + cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range; + cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range; + cp_mix[3] = ((Blend)(cp_dst[3]) * fac + mfac * cp_src[3]) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_saturation(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / Traits::frange, + cp_src[1] / Traits::frange, + cp_src[2] / Traits::frange, + &h1, + &s1, + &v1); + rgb_to_hsv(cp_dst[0] / Traits::frange, + cp_dst[1] / Traits::frange, + cp_dst[2] / Traits::frange, + &h2, + &s2, + &v2); + + if (s1 > EPS_SATURATION) { + s1 = s2; + } + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range; + cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range; + cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_hue(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / Traits::frange, + cp_src[1] / Traits::frange, + cp_src[2] / Traits::frange, + &h1, + &s1, + &v1); + rgb_to_hsv(cp_dst[0] / Traits::frange, + cp_dst[1] / Traits::frange, + cp_dst[2] / Traits::frange, + &h2, + &s2, + &v2); + + h1 = h2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range; + cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range; + cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range; + cp_mix[3] = ((Blend)(cp_dst[3]) * fac + mfac * cp_src[3]) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_alpha_add(Color col_src, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_mix; + Blend temp; + Color col_mix = col_src; + + if (fac == 0) { + return col_src; + } + + cp_src = (Value *)&col_src; + cp_mix = (Value *)&col_mix; + + temp = cp_src[3] + fac; + cp_mix[3] = (temp > Traits::cmpRange) ? Traits::range : temp; + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_alpha_sub(Color col_src, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_mix; + Blend temp; + Color col_mix = col_src; + + if (fac == 0) { + return col_src; + } + + cp_src = (Value *)&col_src; + cp_mix = (Value *)&col_mix; + + temp = cp_src[3] - fac; + cp_mix[3] = temp < 0 ? 0 : temp; + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_pinlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + const Blend cmp = Traits::range / 2; + + int i = 3; + Blend temp; + + while (i--) { + if (cp_dst[i] > cmp) { + temp = Traits::max(2 * (cp_dst[i] - cmp), cp_src[i]); + } + else { + temp = Traits::min(2 * cp_dst[i], cp_src[i]); + } + cp_mix[i] = (Value)((Traits::min(temp, Traits::range) * fac + cp_src[i] * mfac) / + Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_linearlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + const Blend cmp = Traits::range / 2; + + int i = 3; + while (i--) { + Blend temp; + + if (cp_dst[i] > cmp) { + temp = Traits::min(cp_src[i] + 2 * (cp_dst[i] - cmp), Traits::range); + } + else { + temp = Traits::max(cp_src[i] + 2 * cp_dst[i] - Traits::range, 0); + } + + cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_vividlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + + const Blend cmp = Traits::range / 2; + + int i = 3; + + while (i--) { + Blend temp; + + if (cp_dst[i] == Traits::range) { + temp = (cp_src[i] == 0) ? cmp : Traits::range; + } + else if (cp_dst[i] == 0) { + temp = (cp_src[i] == Traits::range) ? cmp : 0; + } + else if (cp_dst[i] > cmp) { + temp = Traits::min(((cp_src[i]) * Traits::range) / (2 * (Traits::range - cp_dst[i])), + Traits::range); + } + else { + temp = Traits::max( + Traits::range - ((Traits::range - cp_src[i]) * Traits::range / (2 * cp_dst[i])), 0); + } + col_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_color(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + + rgb_to_hsv(cp_src[0] / Traits::frange, + cp_src[1] / Traits::frange, + cp_src[2] / Traits::frange, + &h1, + &s1, + &v1); + rgb_to_hsv(cp_dst[0] / Traits::frange, + cp_dst[1] / Traits::frange, + cp_dst[2] / Traits::frange, + &h2, + &s2, + &v2); + + h1 = h2; + s1 = s2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp_mix[0] = (Value)(((Blend)(r * Traits::frange) * fac + cp_src[0] * mfac) / Traits::range); + cp_mix[1] = (Value)(((Blend)(g * Traits::frange) * fac + cp_src[1] * mfac) / Traits::range); + cp_mix[2] = (Value)(((Blend)(b * Traits::frange) * fac + cp_src[2] * mfac) / Traits::range); + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_colorburn(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 3; + + while (i--) { + const Blend temp = + (cp_dst[i] == 0) ? + 0 : + Traits::max(Traits::range - ((Traits::range - cp_src[i]) * Traits::range) / cp_dst[i], + 0); + cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_linearburn(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 3; + + while (i--) { + const Blend temp = Traits::max(cp_src[i] + cp_dst[i] - Traits::range, 0); + cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +BLI_INLINE Color BLI_mix_colors(const IMB_BlendMode tool, + const Color a, + const Color b, + const typename Traits::BlendType alpha) +{ + switch ((IMB_BlendMode)tool) { + case IMB_BLEND_MIX: + return mix_blend<Color, Traits>(a, b, alpha); + case IMB_BLEND_ADD: + return mix_add<Color, Traits>(a, b, alpha); + case IMB_BLEND_SUB: + return mix_sub<Color, Traits>(a, b, alpha); + case IMB_BLEND_MUL: + return mix_mul<Color, Traits>(a, b, alpha); + case IMB_BLEND_LIGHTEN: + return mix_lighten<Color, Traits>(a, b, alpha); + case IMB_BLEND_DARKEN: + return mix_darken<Color, Traits>(a, b, alpha); + case IMB_BLEND_COLORDODGE: + return mix_colordodge<Color, Traits>(a, b, alpha); + case IMB_BLEND_COLORBURN: + return mix_colorburn<Color, Traits>(a, b, alpha); + case IMB_BLEND_DIFFERENCE: + return mix_difference<Color, Traits>(a, b, alpha); + case IMB_BLEND_SCREEN: + return mix_screen<Color, Traits>(a, b, alpha); + case IMB_BLEND_HARDLIGHT: + return mix_hardlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_OVERLAY: + return mix_overlay<Color, Traits>(a, b, alpha); + case IMB_BLEND_SOFTLIGHT: + return mix_softlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_EXCLUSION: + return mix_exclusion<Color, Traits>(a, b, alpha); + case IMB_BLEND_LUMINOSITY: + return mix_luminosity<Color, Traits>(a, b, alpha); + case IMB_BLEND_SATURATION: + return mix_saturation<Color, Traits>(a, b, alpha); + case IMB_BLEND_HUE: + return mix_hue<Color, Traits>(a, b, alpha); + /* non-color */ + case IMB_BLEND_ERASE_ALPHA: + return mix_alpha_sub<Color, Traits>(a, alpha); + case IMB_BLEND_ADD_ALPHA: + return mix_alpha_add<Color, Traits>(a, alpha); + case IMB_BLEND_PINLIGHT: + return mix_pinlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_LINEARLIGHT: + return mix_linearlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_VIVIDLIGHT: + return mix_vividlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_COLOR: + return mix_color<Color, Traits>(a, b, alpha); + default: + BLI_assert(0); + return Color(0, 0, 0, 0); + } +} +/** \} */ + +} // namespace blender::color diff --git a/source/blender/blenlib/BLI_generic_virtual_array.hh b/source/blender/blenlib/BLI_generic_virtual_array.hh index 4aed1caf796..2e756e912f9 100644 --- a/source/blender/blenlib/BLI_generic_virtual_array.hh +++ b/source/blender/blenlib/BLI_generic_virtual_array.hh @@ -854,14 +854,14 @@ template<typename T> inline GVArray::GVArray(const VArray<T> &varray) if (varray.may_have_ownership()) { *this = GVArray::For<GVArrayImpl_For_VArray<T>>(varray); } - else if (varray.is_span()) { - Span<T> data = varray.get_internal_span(); - *this = GVArray::ForSpan(data); - } else if (varray.is_single()) { T value = varray.get_internal_single(); *this = GVArray::ForSingle(CPPType::get<T>(), varray.size(), &value); } + else if (varray.is_span()) { + Span<T> data = varray.get_internal_span(); + *this = GVArray::ForSpan(data); + } else { *this = GVArray::For<GVArrayImpl_For_VArray<T>>(varray); } @@ -880,15 +880,15 @@ template<typename T> inline VArray<T> GVArray::typed() const if (this->may_have_ownership()) { return VArray<T>::template For<VArrayImpl_For_GVArray<T>>(*this); } - if (this->is_span()) { - const Span<T> span = this->get_internal_span().typed<T>(); - return VArray<T>::ForSpan(span); - } if (this->is_single()) { T value; this->get_internal_single(&value); return VArray<T>::ForSingle(value, this->size()); } + if (this->is_span()) { + const Span<T> span = this->get_internal_span().typed<T>(); + return VArray<T>::ForSpan(span); + } return VArray<T>::template For<VArrayImpl_For_GVArray<T>>(*this); } diff --git a/source/blender/blenlib/BLI_math_base.hh b/source/blender/blenlib/BLI_math_base.hh index 83f414f853a..81f5343056e 100644 --- a/source/blender/blenlib/BLI_math_base.hh +++ b/source/blender/blenlib/BLI_math_base.hh @@ -102,7 +102,10 @@ template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(cons return a - std::floor(a); } -template<typename T, typename FactorT, BLI_ENABLE_IF((is_math_float_type<FactorT>))> +template<typename T, + typename FactorT, + BLI_ENABLE_IF((std::is_arithmetic_v<T>)), + BLI_ENABLE_IF((is_math_float_type<FactorT>))> inline T interpolate(const T &a, const T &b, const FactorT &t) { return a * (1 - t) + b * t; diff --git a/source/blender/blenlib/BLI_math_color.hh b/source/blender/blenlib/BLI_math_color.hh index 5195cfb6238..b16053509cf 100644 --- a/source/blender/blenlib/BLI_math_color.hh +++ b/source/blender/blenlib/BLI_math_color.hh @@ -15,9 +15,22 @@ namespace blender::math { -inline ColorGeometry4f interpolate(const ColorGeometry4f &a, - const ColorGeometry4f &b, - const float t) +template<eAlpha Alpha> +inline ColorSceneLinear4f<Alpha> interpolate(const ColorSceneLinear4f<Alpha> &a, + const ColorSceneLinear4f<Alpha> &b, + const float t) +{ + return {math::interpolate(a.r, b.r, t), + math::interpolate(a.g, b.g, t), + math::interpolate(a.b, b.b, t), + math::interpolate(a.a, b.a, t)}; +} + +template<eAlpha Alpha> +inline ColorSceneLinearByteEncoded4b<Alpha> interpolate( + const ColorSceneLinearByteEncoded4b<Alpha> &a, + const ColorSceneLinearByteEncoded4b<Alpha> &b, + const float t) { return {math::interpolate(a.r, b.r, t), math::interpolate(a.g, b.g, t), diff --git a/source/blender/blenlib/BLI_math_vec_types.hh b/source/blender/blenlib/BLI_math_vec_types.hh index 0850864d86f..e36bbedee32 100644 --- a/source/blender/blenlib/BLI_math_vec_types.hh +++ b/source/blender/blenlib/BLI_math_vec_types.hh @@ -201,6 +201,14 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> } } + template<typename U, BLI_ENABLE_IF((std::is_convertible_v<U, T>))> + explicit vec_base(const U *ptr) + { + for (int i = 0; i < Size; i++) { + (*this)[i] = ptr[i]; + } + } + vec_base(const T (*ptr)[Size]) : vec_base(static_cast<const T *>(ptr[0])) { } diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 27bb04f5796..453ca67b1e0 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -780,9 +780,6 @@ template<typename T> class VArrayCommon { Span<T> get_internal_span() const { BLI_assert(this->is_span()); - if (this->is_empty()) { - return {}; - } return impl_->get_internal_span(); } @@ -800,9 +797,6 @@ template<typename T> class VArrayCommon { T get_internal_single() const { BLI_assert(this->is_single()); - if (impl_->size() == 1) { - return impl_->get(0); - } return impl_->get_internal_single(); } diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e8a3851e082..446a027b03b 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -171,6 +171,7 @@ set(SRC BLI_boxpack_2d.h BLI_buffer.h BLI_color.hh + BLI_color_mix.hh BLI_compiler_attrs.h BLI_compiler_compat.h BLI_compiler_typecheck.h diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 26a0479f445..5ca6fe2efd0 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -301,56 +301,60 @@ static bool delete_soft(const wchar_t *path_16, const char **error_message) /* Deletes file or directory to recycling bin. The latter moves all contained files and * directories recursively to the recycling bin as well. */ IFileOperation *pfo; - IShellItem *pSI; + IShellItem *psi; HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - if (FAILED(hr)) { - *error_message = "Failed to initialize COM"; - goto error_1; - } - - hr = CoCreateInstance( - &CLSID_FileOperation, NULL, CLSCTX_ALL, &IID_IFileOperation, (void **)&pfo); - if (FAILED(hr)) { - *error_message = "Failed to create FileOperation instance"; - goto error_2; - } - - /* Flags for deletion: - * FOF_ALLOWUNDO: Enables moving file to recycling bin. - * FOF_SILENT: Don't show progress dialog box. - * FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */ - hr = pfo->lpVtbl->SetOperationFlags(pfo, FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING); - - if (FAILED(hr)) { - *error_message = "Failed to set operation flags"; - goto error_2; - } - - hr = SHCreateItemFromParsingName(path_16, NULL, &IID_IShellItem, (void **)&pSI); - if (FAILED(hr)) { - *error_message = "Failed to parse path"; - goto error_2; - } - - hr = pfo->lpVtbl->DeleteItem(pfo, pSI, NULL); - if (FAILED(hr)) { - *error_message = "Failed to prepare delete operation"; - goto error_2; + if (SUCCEEDED(hr)) { + /* This is also the case when COM was previously initialized and CoInitializeEx returns + * S_FALSE, which is not an error. Both HRESULT values S_OK and S_FALSE indicate success. */ + + hr = CoCreateInstance( + &CLSID_FileOperation, NULL, CLSCTX_ALL, &IID_IFileOperation, (void **)&pfo); + + if (SUCCEEDED(hr)) { + /* Flags for deletion: + * FOF_ALLOWUNDO: Enables moving file to recycling bin. + * FOF_SILENT: Don't show progress dialog box. + * FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */ + hr = pfo->lpVtbl->SetOperationFlags(pfo, FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING); + + if (SUCCEEDED(hr)) { + hr = SHCreateItemFromParsingName(path_16, NULL, &IID_IShellItem, (void **)&psi); + + if (SUCCEEDED(hr)) { + hr = pfo->lpVtbl->DeleteItem(pfo, psi, NULL); + + if (SUCCEEDED(hr)) { + hr = pfo->lpVtbl->PerformOperations(pfo); + + if (FAILED(hr)) { + *error_message = "Failed to prepare delete operation"; + } + } + else { + *error_message = "Failed to prepare delete operation"; + } + psi->lpVtbl->Release(psi); + } + else { + *error_message = "Failed to parse path"; + } + } + else { + *error_message = "Failed to set operation flags"; + } + pfo->lpVtbl->Release(pfo); + } + else { + *error_message = "Failed to create FileOperation instance"; + } + CoUninitialize(); } - - hr = pfo->lpVtbl->PerformOperations(pfo); - - if (FAILED(hr)) { - *error_message = "Failed to delete file or directory"; + else { + *error_message = "Failed to initialize COM"; } -error_2: - pfo->lpVtbl->Release(pfo); - CoUninitialize(); /* Has to be uninitialized when CoInitializeEx returns either S_OK or S_FALSE - */ -error_1: return FAILED(hr); } |