Blender V5.0
bitmap_draw_2d.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <algorithm>
12#include <climits>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_bitmap_draw_2d.h"
17
18#include "BLI_math_base.h"
19#include "BLI_sort.h"
20#include "BLI_utildefines.h"
21
22#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
23
24using blender::int2;
25using blender::Span;
26
27/* -------------------------------------------------------------------- */
30
31void BLI_bitmap_draw_2d_line_v2v2i(const int p1[2],
32 const int p2[2],
33 bool (*callback)(int, int, void *),
34 void *user_data)
35{
36 /* Bresenham's line algorithm. */
37 int x1 = p1[0];
38 int y1 = p1[1];
39 int x2 = p2[0];
40 int y2 = p2[1];
41
42 if (callback(x1, y1, user_data) == 0) {
43 return;
44 }
45
46 /* if x1 == x2 or y1 == y2, then it does not matter what we set here */
47 const int sign_x = (x2 > x1) ? 1 : -1;
48 const int sign_y = (y2 > y1) ? 1 : -1;
49
50 const int delta_x = (sign_x == 1) ? (x2 - x1) : (x1 - x2);
51 const int delta_y = (sign_y == 1) ? (y2 - y1) : (y1 - y2);
52
53 const int delta_x_step = delta_x * 2;
54 const int delta_y_step = delta_y * 2;
55
56 if (delta_x >= delta_y) {
57 /* error may go below zero */
58 int error = delta_y_step - delta_x;
59
60 while (x1 != x2) {
61 if (error >= 0) {
62 if (error || (sign_x == 1)) {
63 y1 += sign_y;
64 error -= delta_x_step;
65 }
66 /* else do nothing */
67 }
68 /* else do nothing */
69
70 x1 += sign_x;
71 error += delta_y_step;
72
73 if (callback(x1, y1, user_data) == 0) {
74 return;
75 }
76 }
77 }
78 else {
79 /* error may go below zero */
80 int error = delta_x_step - delta_y;
81
82 while (y1 != y2) {
83 if (error >= 0) {
84 if (error || (sign_y == 1)) {
85 x1 += sign_x;
86 error -= delta_y_step;
87 }
88 /* else do nothing */
89 }
90 /* else do nothing */
91
92 y1 += sign_y;
93 error += delta_x_step;
94
95 if (callback(x1, y1, user_data) == 0) {
96 return;
97 }
98 }
99 }
100}
101
103
104/* -------------------------------------------------------------------- */
107
123
124/* Macros could be moved to a shared location. */
125#define ORDERED_SWAP(ty, a, b) \
126 if (a > b) { \
127 SWAP(ty, a, b); \
128 } \
129 ((void)0)
130
131#define ORDERED_SWAP_BY(ty, a, b, by) \
132 if ((a by) > (b by)) { \
133 SWAP(ty, a, b); \
134 } \
135 ((void)0)
136
137#define ORDER_VARS2(ty, a, b) \
138 { \
139 ORDERED_SWAP(ty, a, b); \
140 } \
141 ((void)0)
142
143#define ORDER_VARS3_BY(ty, a, b, c, by) \
144 { \
145 ORDERED_SWAP_BY(ty, b, c, by); \
146 ORDERED_SWAP_BY(ty, a, c, by); \
147 ORDERED_SWAP_BY(ty, a, b, by); \
148 } \
149 ((void)0)
150
151static float inv_slope(const int a[2], const int b[2])
152{
153 return float(a[0] - b[0]) / float(a[1] - b[1]);
154}
155
163static void draw_tri_flat_max(const int p[2],
164 const int max_y,
165 const float inv_slope1,
166 const float inv_slope2,
167 void (*callback)(int x, int x_end, int y, void *),
168 void *user_data)
169{
170 float cur_x1 = float(p[0]);
171 float cur_x2 = cur_x1;
172 /* start-end inclusive */
173 const int min_y = p[1];
174 const int max_y_end = max_y + 1;
175 for (int scanline_y = min_y; scanline_y != max_y_end; scanline_y += 1) {
176 callback(int(cur_x1), 1 + int(cur_x2), scanline_y, user_data);
177 cur_x1 += inv_slope1;
178 cur_x2 += inv_slope2;
179 }
180}
181
189static void draw_tri_flat_min(const int p[2],
190 const int min_y,
191 const float inv_slope1,
192 const float inv_slope2,
193 void (*callback)(int x, int x_end, int y, void *),
194 void *user_data)
195{
196 float cur_x1 = float(p[0]);
197 float cur_x2 = cur_x1;
198 /* start-end inclusive */
199 const int max_y = p[1];
200 const int min_y_end = min_y - 1;
201 for (int scanline_y = max_y; scanline_y != min_y_end; scanline_y -= 1) {
202 callback(int(cur_x1), 1 + int(cur_x2), scanline_y, user_data);
203 cur_x1 -= inv_slope1;
204 cur_x2 -= inv_slope2;
205 }
206}
207
209 /* all 2d */
210 const int p1[2],
211 const int p2[2],
212 const int p3[2],
213 void (*callback)(int x, int x_end, int y, void *),
214 void *user_data)
215{
216 /* At first sort the three vertices by y-coordinate ascending so p1 is the top-most vertex */
217 ORDER_VARS3_BY(const int *, p1, p2, p3, [1]);
218
219 BLI_assert(p1[1] <= p2[1] && p2[1] <= p3[1]);
220
221 /* Check for trivial case of bottom-flat triangle. */
222 if (p2[1] == p3[1]) {
223 float inv_slope1 = inv_slope(p2, p1);
224 float inv_slope2 = inv_slope(p3, p1);
225 ORDER_VARS2(float, inv_slope1, inv_slope2);
226 BLI_assert(!(inv_slope1 > inv_slope2));
227 draw_tri_flat_max(p1, p2[1], inv_slope1, inv_slope2, callback, user_data);
228 }
229 else if (p1[1] == p2[1]) {
230 /* Check for trivial case of top-flat triangle. */
231 float inv_slope1 = inv_slope(p3, p1);
232 float inv_slope2 = inv_slope(p3, p2);
233 ORDER_VARS2(float, inv_slope2, inv_slope1);
234 BLI_assert(!(inv_slope1 < inv_slope2));
236 p2[1] + 1, /* avoid overlap */
237 inv_slope1,
238 inv_slope2,
239 callback,
240 user_data);
241 }
242 else {
243 /* General case - split the triangle in a top-flat and bottom-flat one. */
244 const float inv_slope_p21 = inv_slope(p2, p1);
245 const float inv_slope_p31 = inv_slope(p3, p1);
246 const float inv_slope_p32 = inv_slope(p3, p2);
247
248 float inv_slope1_max, inv_slope2_max;
249 float inv_slope2_min, inv_slope1_min;
250
251 if (inv_slope_p21 < inv_slope_p31) {
252 inv_slope1_max = inv_slope_p21;
253 inv_slope2_max = inv_slope_p31;
254 inv_slope2_min = inv_slope_p31;
255 inv_slope1_min = inv_slope_p32;
256 }
257 else {
258 inv_slope1_max = inv_slope_p31;
259 inv_slope2_max = inv_slope_p21;
260 inv_slope2_min = inv_slope_p32;
261 inv_slope1_min = inv_slope_p31;
262 }
263
264 draw_tri_flat_max(p1, p2[1], inv_slope1_max, inv_slope2_max, callback, user_data);
266 p2[1] + 1, /* avoid overlap */
267 inv_slope1_min,
268 inv_slope2_min,
269 callback,
270 user_data);
271 }
272}
273
274#undef ORDERED_SWAP
275#undef ORDERED_SWAP_BY
276#undef ORDER_VARS2
277#undef ORDER_VARS3_BY
278
280
281/* -------------------------------------------------------------------- */
284
285/* sort edge-segments on y, then x axis */
286static int draw_poly_v2i_n__span_y_sort(const void *a_p, const void *b_p, void *verts_p)
287{
288 const int (*verts)[2] = static_cast<const int (*)[2]>(verts_p);
289 const int *a = static_cast<const int *>(a_p);
290 const int *b = static_cast<const int *>(b_p);
291 const int *co_a = verts[a[0]];
292 const int *co_b = verts[b[0]];
293
294 if (co_a[1] < co_b[1]) {
295 return -1;
296 }
297 if (co_a[1] > co_b[1]) {
298 return 1;
299 }
300 if (co_a[0] < co_b[0]) {
301 return -1;
302 }
303 if (co_a[0] > co_b[0]) {
304 return 1;
305 }
306 /* co_a & co_b are identical, use the line closest to the x-min */
307 const int *co = co_a;
308 co_a = verts[a[1]];
309 co_b = verts[b[1]];
310 int ord = (((co_b[0] - co[0]) * (co_a[1] - co[1])) - ((co_a[0] - co[0]) * (co_b[1] - co[1])));
311 if (ord > 0) {
312 return -1;
313 }
314 if (ord < 0) {
315 return 1;
316 }
317 return 0;
318}
319
321 const int ymin,
322 const int xmax,
323 const int ymax,
324 const Span<int2> verts,
325 void (*callback)(int x, int x_end, int y, void *),
326 void *user_data)
327{
328 /* Originally by Darel Rex Finley, 2007.
329 * Optimized by Campbell Barton, 2016 to track sorted intersections. */
330
331 int (*span_y)[2] = MEM_malloc_arrayN<int[2]>(size_t(verts.size()), __func__);
332 int span_y_len = 0;
333
334 for (int i_curr = 0, i_prev = int(verts.size() - 1); i_curr < verts.size(); i_prev = i_curr++) {
335 const int *co_prev = verts[i_prev];
336 const int *co_curr = verts[i_curr];
337
338 if (co_prev[1] != co_curr[1]) {
339 /* Any segments entirely above or below the area of interest can be skipped. */
340 if ((min_ii(co_prev[1], co_curr[1]) >= ymax) || (max_ii(co_prev[1], co_curr[1]) < ymin)) {
341 continue;
342 }
343
344 int *s = span_y[span_y_len++];
345 if (co_prev[1] < co_curr[1]) {
346 s[0] = i_prev;
347 s[1] = i_curr;
348 }
349 else {
350 s[0] = i_curr;
351 s[1] = i_prev;
352 }
353 }
354 }
355
356 BLI_qsort_r(span_y,
357 size_t(span_y_len),
358 sizeof(*span_y),
360 (void *)verts.data());
361
362 struct NodeX {
363 int span_y_index;
364 int x;
365 } *node_x = MEM_malloc_arrayN<NodeX>(size_t(verts.size() + 1), __func__);
366 int node_x_len = 0;
367
368 int span_y_index = 0;
369 if (span_y_len != 0 && verts[span_y[0][0]][1] < ymin) {
370 while ((span_y_index < span_y_len) && (verts[span_y[span_y_index][0]][1] < ymin)) {
371 BLI_assert(verts[span_y[span_y_index][0]][1] < verts[span_y[span_y_index][1]][1]);
372 if (verts[span_y[span_y_index][1]][1] >= ymin) {
373 NodeX *n = &node_x[node_x_len++];
374 n->span_y_index = span_y_index;
375 }
376 span_y_index += 1;
377 }
378 }
379
380 /* Loop through the rows of the image. */
381 for (int pixel_y = ymin; pixel_y < ymax; pixel_y++) {
382 bool is_sorted = true;
383 bool do_remove = false;
384
385 for (int i = 0, x_ix_prev = INT_MIN; i < node_x_len; i++) {
386 NodeX *n = &node_x[i];
387 const int *s = span_y[n->span_y_index];
388 const int *co_prev = verts[s[0]];
389 const int *co_curr = verts[s[1]];
390
391 BLI_assert(co_prev[1] < pixel_y && co_curr[1] >= pixel_y);
392
393 const double x = (co_prev[0] - co_curr[0]);
394 const double y = (co_prev[1] - co_curr[1]);
395 const double y_px = (pixel_y - co_curr[1]);
396 const int x_ix = int(double(co_curr[0]) + ((y_px / y) * x));
397 n->x = x_ix;
398
399 if (is_sorted && (x_ix_prev > x_ix)) {
400 is_sorted = false;
401 }
402 if (do_remove == false && co_curr[1] == pixel_y) {
403 do_remove = true;
404 }
405 x_ix_prev = x_ix;
406 }
407
408 /* Sort the nodes, via a simple "Bubble" sort. */
409 if (is_sorted == false) {
410 int i = 0;
411 const int node_x_end = node_x_len - 1;
412 while (i < node_x_end) {
413 if (node_x[i].x > node_x[i + 1].x) {
414 SWAP(NodeX, node_x[i], node_x[i + 1]);
415 if (i != 0) {
416 i -= 1;
417 }
418 }
419 else {
420 i += 1;
421 }
422 }
423 }
424
425 /* Fill the pixels between node pairs. */
426 for (int i = 0; i < node_x_len; i += 2) {
427 int x_src = node_x[i].x;
428 int x_dst = node_x[i + 1].x;
429
430 if (x_src >= xmax) {
431 break;
432 }
433
434 if (x_dst > xmin) {
435 x_src = std::max(x_src, xmin);
436 x_dst = std::min(x_dst, xmax);
437 /* for single call per x-span */
438 if (x_src < x_dst) {
439 callback(x_src - xmin, x_dst - xmin, pixel_y - ymin, user_data);
440 }
441 }
442 }
443
444 /* Clear finalized nodes in one pass, only when needed
445 * (avoids excessive array-resizing). */
446 if (do_remove == true) {
447 int i_dst = 0;
448 for (int i_src = 0; i_src < node_x_len; i_src += 1) {
449 const int *s = span_y[node_x[i_src].span_y_index];
450 const int *co = verts[s[1]];
451 if (co[1] != pixel_y) {
452 if (i_dst != i_src) {
453 /* x is initialized for the next pixel_y (no need to adjust here) */
454 node_x[i_dst].span_y_index = node_x[i_src].span_y_index;
455 }
456 i_dst += 1;
457 }
458 }
459 node_x_len = i_dst;
460 }
461
462 /* Scan for new x-nodes */
463 while ((span_y_index < span_y_len) && (verts[span_y[span_y_index][0]][1] == pixel_y)) {
464 /* NOTE: node_x these are just added at the end,
465 * not ideal but sorting once will resolve. */
466
467 /* x is initialized for the next pixel_y */
468 NodeX *n = &node_x[node_x_len++];
469 n->span_y_index = span_y_index;
470 span_y_index += 1;
471 }
472 }
473
474 MEM_freeN(span_y);
475 MEM_freeN(node_x);
476}
477
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
void BLI_qsort_r(void *a, size_t n, size_t es, BLI_sort_cmp_t cmp, void *thunk)
Definition sort.cc:77
#define SWAP(type, a, b)
Read Guarded memory(de)allocation.
void BLI_bitmap_draw_2d_poly_v2i_n(const int xmin, const int ymin, const int xmax, const int ymax, const Span< int2 > verts, void(*callback)(int x, int x_end, int y, void *), void *user_data)
static int draw_poly_v2i_n__span_y_sort(const void *a_p, const void *b_p, void *verts_p)
static float inv_slope(const int a[2], const int b[2])
void BLI_bitmap_draw_2d_line_v2v2i(const int p1[2], const int p2[2], bool(*callback)(int, int, void *), void *user_data)
void BLI_bitmap_draw_2d_tri_v2i(const int p1[2], const int p2[2], const int p3[2], void(*callback)(int x, int x_end, int y, void *), void *user_data)
static void draw_tri_flat_max(const int p[2], const int max_y, const float inv_slope1, const float inv_slope2, void(*callback)(int x, int x_end, int y, void *), void *user_data)
static void draw_tri_flat_min(const int p[2], const int min_y, const float inv_slope1, const float inv_slope2, void(*callback)(int x, int x_end, int y, void *), void *user_data)
#define ORDER_VARS3_BY(ty, a, b, c, by)
#define ORDER_VARS2(ty, a, b)
nullptr float
static float verts[][3]
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
static void error(const char *str)
VecBase< int32_t, 2 > int2
i
Definition text_draw.cc:230