Blender V4.3
grease_pencil_trace_util.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_attribute.hh"
6#include "BLI_color.hh"
7#include "BLI_math_matrix.hh"
8
9#include "BKE_curves.hh"
10
11#include "IMB_imbuf.hh"
12
13#include "MEM_guardedalloc.h"
14
15#ifdef WITH_POTRACE
16# include "potracelib.h"
17#endif
18
20
22
23#ifdef WITH_POTRACE
24
25using PathSegment = potrace_dpoint_t[3];
26
27static int to_potrace(const TurnPolicy turn_policy)
28{
29 switch (turn_policy) {
30 case TurnPolicy ::Foreground:
31 return POTRACE_TURNPOLICY_BLACK;
32 case TurnPolicy ::Background:
33 return POTRACE_TURNPOLICY_WHITE;
34 case TurnPolicy ::Left:
35 return POTRACE_TURNPOLICY_LEFT;
36 case TurnPolicy ::Right:
37 return POTRACE_TURNPOLICY_RIGHT;
38 case TurnPolicy ::Minority:
39 return POTRACE_TURNPOLICY_MINORITY;
40 case TurnPolicy ::Majority:
41 return POTRACE_TURNPOLICY_MAJORITY;
42 case TurnPolicy ::Random:
43 return POTRACE_TURNPOLICY_RANDOM;
44 }
46 return POTRACE_TURNPOLICY_MINORITY;
47}
48
49Bitmap *create_bitmap(const int2 &size)
50{
51# ifdef WITH_POTRACE
52 constexpr int BM_WORDSIZE = int(sizeof(potrace_word));
53 constexpr int BM_WORDBITS = 8 * BM_WORDSIZE;
54
55 /* Number of words per scanline. */
56 const int32_t dy = (size.x + BM_WORDBITS - 1) / BM_WORDBITS;
57
58 potrace_bitmap_t *bm = static_cast<potrace_bitmap_t *>(
59 MEM_mallocN(sizeof(potrace_bitmap_t), __func__));
60 if (!bm) {
61 return nullptr;
62 }
63 bm->w = size.x;
64 bm->h = size.y;
65 bm->dy = dy;
66 bm->map = static_cast<potrace_word *>(MEM_mallocN(size.y * dy * sizeof(potrace_word), __func__));
67
68 return bm;
69# else
70 UNUSED_VARS(size);
71 return nullptr;
72# endif
73}
74
75void free_bitmap(Bitmap *bm)
76{
77# ifdef WITH_POTRACE
78 if (bm != nullptr) {
79 MEM_freeN(bm->map);
80 }
82# else
84# endif
85}
86
87ImBuf *bitmap_to_image(const Bitmap &bm)
88{
89 constexpr int BM_WORDSIZE = int(sizeof(potrace_word));
90 constexpr int BM_WORDBITS = 8 * BM_WORDSIZE;
91 constexpr potrace_word BM_HIBIT = potrace_word(1) << (BM_WORDBITS - 1);
92
93 const int2 size = {bm.w, bm.h};
94 const uint imb_flag = IB_rect;
95 ImBuf *ibuf = IMB_allocImBuf(size.x, size.y, 32, imb_flag);
96 BLI_assert(ibuf->byte_buffer.data != nullptr);
97
98 const int num_words = bm.dy * bm.h;
99 const int words_per_scanline = bm.dy;
100 const Span<potrace_word> words = {bm.map, num_words};
102 reinterpret_cast<ColorGeometry4b *>(ibuf->byte_buffer.data), ibuf->x * ibuf->y};
103 threading::parallel_for(IndexRange(ibuf->y), 4096, [&](const IndexRange range) {
104 for (const int y : range) {
105 Span<potrace_word> scanline_words = words.slice(
106 IndexRange(words_per_scanline * y, words_per_scanline));
107 const MutableSpan<ColorGeometry4b> scanline_colors = colors.slice(
108 IndexRange(y * ibuf->x, ibuf->x));
109 for (uint32_t x = 0; x < ibuf->x; x++) {
110 const potrace_word &word = scanline_words[x / BM_WORDBITS];
111 const potrace_word mask = BM_HIBIT >> (x & (BM_WORDBITS - 1));
112 scanline_colors[x] = ((word & mask) != 0 ? ColorGeometry4b(255, 0, 0, 255) :
113 ColorGeometry4b(0, 0, 255, 255));
114 }
115 }
116 });
117
118 return ibuf;
119}
120
121Trace *trace_bitmap(const TraceParams &params, Bitmap &bm)
122{
123 potrace_param_t *po_params = potrace_param_default();
124 if (!po_params) {
125 return nullptr;
126 }
127 po_params->turdsize = params.size_threshold;
128 po_params->turnpolicy = to_potrace(params.turn_policy);
129 po_params->alphamax = params.alpha_max;
130 po_params->opticurve = params.optimize_curves;
131 po_params->opttolerance = params.optimize_tolerance;
132
133 potrace_state_t *st = potrace_trace(po_params, &bm);
134 potrace_param_free(po_params);
135
136 if (!st || st->status != POTRACE_STATUS_OK) {
137 if (st) {
138 potrace_state_free(st);
139 }
140 return nullptr;
141 }
142 return st;
143}
144
145void free_trace(Trace *trace)
146{
147 potrace_state_free(trace);
148}
149bke::CurvesGeometry trace_to_curves(const Trace &trace,
150 const StringRef hole_attribute_id,
151 const float4x4 &transform)
152{
153
154 return trace_to_curves(trace, hole_attribute_id, [=](const int2 &pixel) {
155 return math::transform_point(transform, float3(pixel.x, pixel.y, 0));
156 });
157}
158
159bke::CurvesGeometry trace_to_curves(const Trace &trace,
160 const StringRef hole_attribute_id,
161 FunctionRef<float3(const int2 &)> pixel_to_position)
162{
163 auto project_pixel = [&](const potrace_dpoint_t &point) -> float3 {
164 return pixel_to_position(int2(point.x, point.y));
165 };
166
167 /* Count paths and points. */
168 Vector<int> offsets;
169 for (const potrace_path_t *path = trace.plist; path != nullptr; path = path->next) {
170 const Span<int> path_tags = {path->curve.tag, path->curve.n};
171 const Span<PathSegment> path_segments = {path->curve.c, path->curve.n};
172
173 int point_num = 0;
174 for (const int segment_i : path_segments.index_range()) {
175 switch (path_tags[segment_i]) {
176 case POTRACE_CORNER:
177 point_num += 2;
178 break;
179 case POTRACE_CURVETO:
180 point_num += 1;
181 break;
182 default:
184 break;
185 }
186 }
187 offsets.append(point_num);
188 }
189 /* Last element stores total size. */
190 offsets.append(0);
191 const OffsetIndices points_by_curve = offset_indices::accumulate_counts_to_offsets(offsets);
192 if (points_by_curve.is_empty()) {
193 return {};
194 }
195
196 bke::CurvesGeometry curves(points_by_curve.total_size(), points_by_curve.size());
197 curves.offsets_for_write().copy_from(offsets);
198
199 /* Construct all curves as Bezier curves. */
200 curves.curve_types_for_write().fill(CURVE_TYPE_BEZIER);
201 curves.update_curve_types();
202 /* All trace curves are cyclic. */
203 curves.cyclic_for_write().fill(true);
204
205 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
206 MutableSpan<int8_t> handle_types_left = curves.handle_types_left_for_write();
207 MutableSpan<int8_t> handle_types_right = curves.handle_types_right_for_write();
208 MutableSpan<float3> handle_positions_left = curves.handle_positions_left_for_write();
209 MutableSpan<float3> handle_positions_right = curves.handle_positions_right_for_write();
210 MutableSpan<float3> positions = curves.positions_for_write();
211 bke::SpanAttributeWriter<bool> holes = attributes.lookup_or_add_for_write_span<bool>(
212 hole_attribute_id, bke::AttrDomain::Curve);
213
214 /* Draw each curve. */
215 int curve_i = 0;
216 for (const potrace_path_t *path = trace.plist; path != nullptr; path = path->next, ++curve_i) {
217 const Span<int> path_tags = {path->curve.tag, path->curve.n};
218 const Span<PathSegment> path_segments = {path->curve.c, path->curve.n};
219
220 const IndexRange points = points_by_curve[curve_i];
221 if (points.is_empty()) {
222 continue;
223 }
224
225 /* Mark paths with negative sign as "holes". */
226 if (holes) {
227 holes.span[curve_i] = (path->sign == '-');
228 }
229
230 /* POTRACE stores the last 3 points of a bezier segment.
231 * The start point is the last segment's end point. */
232 int point_i = points.last();
233 auto next_point = [&]() {
234 point_i = (point_i == points.last() ? points.first() : point_i + 1);
235 };
236
237 for (const int segment_i : path_segments.index_range()) {
238 const PathSegment &segment = path_segments[segment_i];
239 switch (path_tags[segment_i]) {
240 case POTRACE_CORNER:
241 /* POTRACE corners are formed by straight lines from the previous/next point.
242 * segment[0] is unused, segment[1] is the corner position, segment[2] is the next point.
243 */
244 handle_types_right[point_i] = BEZIER_HANDLE_VECTOR;
245
246 next_point();
247 positions[point_i] = project_pixel(segment[1]);
248 handle_types_left[point_i] = BEZIER_HANDLE_VECTOR;
249 handle_types_right[point_i] = BEZIER_HANDLE_VECTOR;
250
251 next_point();
252 positions[point_i] = project_pixel(segment[2]);
253 handle_types_left[point_i] = BEZIER_HANDLE_VECTOR;
254 break;
255 case POTRACE_CURVETO:
256 /* segment[0] is the previous point's right-side handle, segment[1] is the next point's
257 * left-side handle, segment[2] is the next point. */
258 handle_types_right[point_i] = BEZIER_HANDLE_FREE;
259 handle_positions_right[point_i] = project_pixel(segment[0]);
260
261 next_point();
262 positions[point_i] = project_pixel(segment[2]);
263 handle_types_left[point_i] = BEZIER_HANDLE_FREE;
264 handle_positions_left[point_i] = project_pixel(segment[1]);
265 break;
266 default:
268 break;
269 }
270 }
271 }
272
273 holes.finish();
274 curves.tag_topology_changed();
275 curves.tag_positions_changed();
276 curves.tag_radii_changed();
277
278 /* Calculate handles for all corner points (vector handle type). */
279 curves.calculate_bezier_auto_handles();
280
281 return curves;
282}
283
284#endif
285
286} // namespace blender::ed::image_trace
Low-level operations for curves.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
unsigned int uint
#define UNUSED_VARS(...)
@ CURVE_TYPE_BEZIER
@ BEZIER_HANDLE_FREE
@ BEZIER_HANDLE_VECTOR
@ IB_rect
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
ATTR_WARN_UNUSED_RESULT BMesh * bm
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void free_bitmap(Bitmap *bm)
Bitmap * create_bitmap(const int2 &size)
bke::CurvesGeometry trace_to_curves(const Trace &trace, StringRef hole_attribute_id, const float4x4 &transform)
void free_trace(Trace *trace)
ImBuf * bitmap_to_image(const Bitmap &bm)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
signed int int32_t
Definition stdint.h:77
ImBufByteBuffer byte_buffer