Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
brush_profile.h
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#ifndef DT_IOP_DRAWLAYER_BRUSH_PROFILE_H
20#define DT_IOP_DRAWLAYER_BRUSH_PROFILE_H
21
22#include "iop/drawlayer/brush.h"
23
24#include <math.h>
25
31static inline float dt_drawlayer_brush_profile_clamp01(const float v)
32{
33 return fminf(fmaxf(v, 0.0f), 1.0f);
34}
35
37static inline float dt_drawlayer_brush_transition_profile_eval(const int shape, const float t, const float inv_t)
38{
39 switch(shape)
40 {
42 return inv_t * inv_t;
44 {
45 const float smooth = t * t * (3.0f - 2.0f * t);
46 return 1.0f - smooth;
47 }
49 default:
50 return inv_t;
51 }
52}
53
55static inline float dt_drawlayer_brush_transition_mass_primitive_eval(const int shape, const float u,
56 const float inner, const float w,
57 const float base)
58{
59 switch(shape)
60 {
62 {
63 const float q_u = 0.5f * u * u - (2.0f / 3.0f) * u * u * u + 0.25f * u * u * u * u;
64 const float q_i = 0.5f * inner * inner - (2.0f / 3.0f) * inner * inner * inner
65 + 0.25f * inner * inner * inner * inner;
66 return base + (q_u - q_i) / (w * w);
67 }
69 {
70 const float s = dt_drawlayer_brush_profile_clamp01((u - inner) / w);
71 const float s2 = s * s;
72 const float s3 = s2 * s;
73 const float s4 = s2 * s2;
74 const float s5 = s4 * s;
75 const float delta = w * (inner * (s - s3 + 0.5f * s4)
76 + w * (0.5f * s2 - 0.75f * s4 + 0.4f * s5));
77 return base + delta;
78 }
80 default:
81 {
82 const float l_u = 0.5f * u * u - (1.0f / 3.0f) * u * u * u;
83 const float l_i = 0.5f * inner * inner - (1.0f / 3.0f) * inner * inner * inner;
84 return base + (l_u - l_i) / w;
85 }
86 }
87}
88
94static inline float dt_drawlayer_brush_profile_eval(const dt_drawlayer_brush_dab_t *dab, const float norm2)
95{
96 if(IS_NULL_PTR(dab) || norm2 >= 1.0f) return 0.0f;
97
99 {
100 const float radius = sqrtf(norm2);
101 if(radius < 0.5f) return 1.0f - 6.0f * norm2 + 6.0f * norm2 * radius;
102 const float inv_r = 1.0f - radius;
103 return 2.0f * inv_r * inv_r * inv_r;
104 }
105
106 const float hardness = dt_drawlayer_brush_profile_clamp01(dab->hardness);
107 if(hardness >= 1.0f - 1e-6f) return 1.0f;
108
109 const float min_inner = 0.5f / fmaxf(dab->radius, 0.5f);
110 const float inner = fmaxf(hardness, dt_drawlayer_brush_profile_clamp01(min_inner));
111 const float radius = sqrtf(norm2);
112 if(radius <= inner) return 1.0f;
113
114 const float t = dt_drawlayer_brush_profile_clamp01((radius - inner) / fmaxf(1.0f - inner, 1e-6f));
116}
117
122static inline float dt_drawlayer_brush_mass_primitive_eval(const dt_drawlayer_brush_dab_t *dab, const float u_in)
123{
124 if(IS_NULL_PTR(dab)) return 0.0f;
125 const float u = dt_drawlayer_brush_profile_clamp01(u_in);
126 if(u <= 0.0f) return 0.0f;
127
129 {
130 if(u <= 0.5f) return 0.5f * u * u - 1.5f * u * u * u * u + 1.2f * u * u * u * u * u;
131 const float u2 = u * u;
132 const float u3 = u2 * u;
133 const float u4 = u2 * u2;
134 const float u5 = u4 * u;
135 return u2 - 2.0f * u3 + 1.5f * u4 - 0.4f * u5 - 0.0125f;
136 }
137
138 const float hardness = dt_drawlayer_brush_profile_clamp01(dab->hardness);
139 if(hardness >= 1.0f - 1e-6f) return 0.5f * u * u;
140 const float min_inner = 0.5f / fmaxf(dab->radius, 0.5f);
141 const float inner = fmaxf(hardness, dt_drawlayer_brush_profile_clamp01(min_inner));
142 if(u <= inner) return 0.5f * u * u;
143 const float w = fmaxf(1.0f - inner, 1e-6f);
144 const float base = 0.5f * inner * inner;
145 return dt_drawlayer_brush_transition_mass_primitive_eval(dab->shape, u, inner, w, base);
146}
147#endif // DT_IOP_DRAWLAYER_BRUSH_PROFILE_H
Dab-level brush rasterization API for drawlayer.
@ DT_DRAWLAYER_BRUSH_SHAPE_SIGMOIDAL
Definition brush.h:43
@ DT_DRAWLAYER_BRUSH_SHAPE_GAUSSIAN
Definition brush.h:41
@ DT_DRAWLAYER_BRUSH_SHAPE_LINEAR
Definition brush.h:40
@ DT_DRAWLAYER_BRUSH_SHAPE_QUADRATIC
Definition brush.h:42
static float dt_drawlayer_brush_profile_eval(const dt_drawlayer_brush_dab_t *dab, const float norm2)
Evaluate normalized brush profile at squared normalized radius.
static float dt_drawlayer_brush_mass_primitive_eval(const dt_drawlayer_brush_dab_t *dab, const float u_in)
Evaluate radial mass primitive from center to normalized radius u_in.
static float dt_drawlayer_brush_profile_clamp01(const float v)
Clamp scalar value to [0, 1].
static float dt_drawlayer_brush_transition_mass_primitive_eval(const int shape, const float u, const float inner, const float w, const float base)
Evaluate integrated mass primitive of transition zone by shape.
static float dt_drawlayer_brush_transition_profile_eval(const int shape, const float t, const float inv_t)
Evaluate normalized edge-falloff transition profile by shape.
const int t
const float v
const float delta
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
const float u2
Fully resolved input dab descriptor.
Definition brush.h:65