25using PathSegment = potrace_dpoint_t[3];
27static int to_potrace(
const TurnPolicy turn_policy)
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;
46 return POTRACE_TURNPOLICY_MINORITY;
52 constexpr int BM_WORDSIZE =
int(
sizeof(potrace_word));
53 constexpr int BM_WORDBITS = 8 * BM_WORDSIZE;
56 const int32_t dy = (size.x + BM_WORDBITS - 1) / BM_WORDBITS;
58 potrace_bitmap_t *
bm =
static_cast<potrace_bitmap_t *
>(
66 bm->map =
static_cast<potrace_word *
>(
MEM_mallocN(size.y * dy *
sizeof(potrace_word), __func__));
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);
98 const int num_words =
bm.dy *
bm.h;
99 const int words_per_scanline =
bm.dy;
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));
121Trace *trace_bitmap(
const TraceParams &
params, Bitmap &
bm)
123 potrace_param_t *po_params = potrace_param_default();
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;
133 potrace_state_t *st = potrace_trace(po_params, &
bm);
134 potrace_param_free(po_params);
136 if (!st || st->status != POTRACE_STATUS_OK) {
138 potrace_state_free(st);
147 potrace_state_free(trace);
150 const StringRef hole_attribute_id,
151 const float4x4 &transform)
160 const StringRef hole_attribute_id,
161 FunctionRef<
float3(
const int2 &)> pixel_to_position)
163 auto project_pixel = [&](
const potrace_dpoint_t &
point) ->
float3 {
164 return pixel_to_position(
int2(point.x, point.y));
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};
174 for (
const int segment_i : path_segments.index_range()) {
175 switch (path_tags[segment_i]) {
179 case POTRACE_CURVETO:
187 offsets.append(point_num);
191 const OffsetIndices points_by_curve = offset_indices::accumulate_counts_to_offsets(offsets);
192 if (points_by_curve.is_empty()) {
196 bke::CurvesGeometry curves(points_by_curve.total_size(), points_by_curve.size());
197 curves.offsets_for_write().copy_from(offsets);
201 curves.update_curve_types();
203 curves.cyclic_for_write().fill(
true);
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);
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};
220 const IndexRange points = points_by_curve[curve_i];
221 if (points.is_empty()) {
227 holes.span[curve_i] = (path->sign ==
'-');
232 int point_i = points.last();
233 auto next_point = [&]() {
234 point_i = (point_i == points.last() ? points.first() : point_i + 1);
237 for (
const int segment_i : path_segments.index_range()) {
238 const PathSegment &segment = path_segments[segment_i];
239 switch (path_tags[segment_i]) {
247 positions[point_i] = project_pixel(segment[1]);
252 positions[point_i] = project_pixel(segment[2]);
255 case POTRACE_CURVETO:
259 handle_positions_right[point_i] = project_pixel(segment[0]);
262 positions[point_i] = project_pixel(segment[2]);
264 handle_positions_left[point_i] = project_pixel(segment[1]);
274 curves.tag_topology_changed();
275 curves.tag_positions_changed();
276 curves.tag_radii_changed();
279 curves.calculate_bezier_auto_handles();