Blender V5.0
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 constexpr int BM_WORDSIZE = int(sizeof(potrace_word));
52 constexpr int BM_WORDBITS = 8 * BM_WORDSIZE;
53
54 /* Number of words per scanline. */
55 const int32_t dy = (size.x + BM_WORDBITS - 1) / BM_WORDBITS;
56
57 potrace_bitmap_t *bm = MEM_mallocN<potrace_bitmap_t>(__func__);
58 if (!bm) {
59 return nullptr;
60 }
61 bm->w = size.x;
62 bm->h = size.y;
63 bm->dy = dy;
64 bm->map = MEM_malloc_arrayN<potrace_word>(size.y * dy, __func__);
65
66 return bm;
67}
68
69void free_bitmap(Bitmap *bm)
70{
71 if (bm != nullptr) {
72 MEM_freeN(bm->map);
73 }
75}
76
77ImBuf *bitmap_to_image(const Bitmap &bm)
78{
79 constexpr int BM_WORDSIZE = int(sizeof(potrace_word));
80 constexpr int BM_WORDBITS = 8 * BM_WORDSIZE;
81 constexpr potrace_word BM_HIBIT = potrace_word(1) << (BM_WORDBITS - 1);
82
83 const int2 size = {bm.w, bm.h};
84 const uint imb_flag = IB_byte_data;
85 ImBuf *ibuf = IMB_allocImBuf(size.x, size.y, 32, imb_flag);
86 BLI_assert(ibuf->byte_buffer.data != nullptr);
87
88 const int num_words = bm.dy * bm.h;
89 const int words_per_scanline = bm.dy;
90 const Span<potrace_word> words = {bm.map, num_words};
92 reinterpret_cast<ColorGeometry4b *>(ibuf->byte_buffer.data),
94 threading::parallel_for(IndexRange(ibuf->y), 4096, [&](const IndexRange range) {
95 for (const int y : range) {
96 Span<potrace_word> scanline_words = words.slice(
97 IndexRange(words_per_scanline * y, words_per_scanline));
98 const MutableSpan<ColorGeometry4b> scanline_colors = colors.slice(
99 IndexRange(y * ibuf->x, ibuf->x));
100 for (uint32_t x = 0; x < ibuf->x; x++) {
101 const potrace_word &word = scanline_words[x / BM_WORDBITS];
102 const potrace_word mask = BM_HIBIT >> (x & (BM_WORDBITS - 1));
103 scanline_colors[x] = ((word & mask) != 0 ? ColorGeometry4b(255, 0, 0, 255) :
104 ColorGeometry4b(0, 0, 255, 255));
105 }
106 }
107 });
108
109 return ibuf;
110}
111
112Trace *trace_bitmap(const TraceParams &params, Bitmap &bm)
113{
114 potrace_param_t *po_params = potrace_param_default();
115 if (!po_params) {
116 return nullptr;
117 }
118 po_params->turdsize = params.size_threshold;
119 po_params->turnpolicy = to_potrace(params.turn_policy);
120 po_params->alphamax = params.alpha_max;
121 po_params->opticurve = params.optimize_curves;
122 po_params->opttolerance = params.optimize_tolerance;
123
124 potrace_state_t *st = potrace_trace(po_params, &bm);
125 potrace_param_free(po_params);
126
127 if (!st || st->status != POTRACE_STATUS_OK) {
128 if (st) {
129 potrace_state_free(st);
130 }
131 return nullptr;
132 }
133 return st;
134}
135
136void free_trace(Trace *trace)
137{
138 potrace_state_free(trace);
139}
140bke::CurvesGeometry trace_to_curves(const Trace &trace,
141 const StringRef hole_attribute_id,
142 const float4x4 &transform)
143{
144
145 return trace_to_curves(trace, hole_attribute_id, [=](const int2 &pixel) {
146 return math::transform_point(transform, float3(pixel.x, pixel.y, 0));
147 });
148}
149
150bke::CurvesGeometry trace_to_curves(const Trace &trace,
151 const StringRef hole_attribute_id,
152 FunctionRef<float3(const int2 &)> pixel_to_position)
153{
154 auto project_pixel = [&](const potrace_dpoint_t &point) -> float3 {
155 return pixel_to_position(int2(point.x, point.y));
156 };
157
158 /* Count paths and points. */
159 Vector<int> offsets;
160 for (const potrace_path_t *path = trace.plist; path != nullptr; path = path->next) {
161 const Span<int> path_tags = {path->curve.tag, path->curve.n};
162 const Span<PathSegment> path_segments = {path->curve.c, path->curve.n};
163
164 int point_num = 0;
165 for (const int segment_i : path_segments.index_range()) {
166 switch (path_tags[segment_i]) {
167 case POTRACE_CORNER:
168 point_num += 2;
169 break;
170 case POTRACE_CURVETO:
171 point_num += 1;
172 break;
173 default:
175 break;
176 }
177 }
178 offsets.append(point_num);
179 }
180 /* Last element stores total size. */
181 offsets.append(0);
182 const OffsetIndices points_by_curve = offset_indices::accumulate_counts_to_offsets(offsets);
183 if (points_by_curve.is_empty()) {
184 return {};
185 }
186
187 bke::CurvesGeometry curves(points_by_curve.total_size(), points_by_curve.size());
188 curves.offsets_for_write().copy_from(offsets);
189
190 /* Construct all curves as Bezier curves. */
191 curves.curve_types_for_write().fill(CURVE_TYPE_BEZIER);
192 curves.update_curve_types();
193 /* All trace curves are cyclic. */
194 curves.cyclic_for_write().fill(true);
195
196 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
197 MutableSpan<int8_t> handle_types_left = curves.handle_types_left_for_write();
198 MutableSpan<int8_t> handle_types_right = curves.handle_types_right_for_write();
199 MutableSpan<float3> handle_positions_left = curves.handle_positions_left_for_write();
200 MutableSpan<float3> handle_positions_right = curves.handle_positions_right_for_write();
201 MutableSpan<float3> positions = curves.positions_for_write();
202 bke::SpanAttributeWriter<bool> holes = attributes.lookup_or_add_for_write_span<bool>(
203 hole_attribute_id, bke::AttrDomain::Curve);
204
205 /* Draw each curve. */
206 int curve_i = 0;
207 for (const potrace_path_t *path = trace.plist; path != nullptr; path = path->next, ++curve_i) {
208 const Span<int> path_tags = {path->curve.tag, path->curve.n};
209 const Span<PathSegment> path_segments = {path->curve.c, path->curve.n};
210
211 const IndexRange points = points_by_curve[curve_i];
212 if (points.is_empty()) {
213 continue;
214 }
215
216 /* Mark paths with negative sign as "holes". */
217 if (holes) {
218 holes.span[curve_i] = (path->sign == '-');
219 }
220
221 /* POTRACE stores the last 3 points of a bezier segment.
222 * The start point is the last segment's end point. */
223 int point_i = points.last();
224 auto next_point = [&]() {
225 point_i = (point_i == points.last() ? points.first() : point_i + 1);
226 };
227
228 for (const int segment_i : path_segments.index_range()) {
229 const PathSegment &segment = path_segments[segment_i];
230 switch (path_tags[segment_i]) {
231 case POTRACE_CORNER:
232 /* POTRACE corners are formed by straight lines from the previous/next point.
233 * segment[0] is unused, segment[1] is the corner position, segment[2] is the next point.
234 */
235 handle_types_right[point_i] = BEZIER_HANDLE_VECTOR;
236
237 next_point();
238 positions[point_i] = project_pixel(segment[1]);
239 handle_types_left[point_i] = BEZIER_HANDLE_VECTOR;
240 handle_types_right[point_i] = BEZIER_HANDLE_VECTOR;
241
242 next_point();
243 positions[point_i] = project_pixel(segment[2]);
244 handle_types_left[point_i] = BEZIER_HANDLE_VECTOR;
245 break;
246 case POTRACE_CURVETO:
247 /* segment[0] is the previous point's right-side handle, segment[1] is the next point's
248 * left-side handle, segment[2] is the next point. */
249 handle_types_right[point_i] = BEZIER_HANDLE_FREE;
250 handle_positions_right[point_i] = project_pixel(segment[0]);
251
252 next_point();
253 positions[point_i] = project_pixel(segment[2]);
254 handle_types_left[point_i] = BEZIER_HANDLE_FREE;
255 handle_positions_left[point_i] = project_pixel(segment[1]);
256 break;
257 default:
259 break;
260 }
261 }
262 }
263
264 holes.finish();
265 curves.tag_topology_changed();
266 curves.tag_positions_changed();
267 curves.tag_radii_changed();
268
269 /* Calculate handles for all corner points (vector handle type). */
270 curves.calculate_bezier_auto_handles();
271
272 return curves;
273}
274
275#endif
276
277} // namespace blender::ed::image_trace
Low-level operations for curves.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
unsigned int uint
@ BEZIER_HANDLE_FREE
@ BEZIER_HANDLE_VECTOR
size_t IMB_get_pixel_count(const ImBuf *ibuf)
Get the length of the data of the given image buffer in pixels.
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
@ IB_byte_data
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
BMesh * bm
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr bool is_empty() const
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
void append(const T &value)
MatBase< 4, 4 > float4x4
VecBase< int, 2 > int2
VecBase< float, 3 > float3
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
Segment< FEdge *, Vec3r > segment
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:93
VecBase< int32_t, 2 > int2
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
ImBufByteBuffer byte_buffer