Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2018-04-21 19:34:34 +0300
committerCampbell Barton <ideasman42@gmail.com>2018-04-21 19:34:34 +0300
commit1e9fb355bf7d4b162e924de24bdeadb197416d1b (patch)
tree76c0676d9e9604d7d4deec4679f5c7edf83de009
parent83cb3879442ab29cbfc7a0c56af48bef823aa826 (diff)
BLI_bitmap: 2D triangle drawing function
Matching polygon filling but no need for allocation or qsort.
-rw-r--r--source/blender/blenlib/BLI_bitmap_draw_2d.h4
-rw-r--r--source/blender/blenlib/intern/bitmap_draw_2d.c184
2 files changed, 186 insertions, 2 deletions
diff --git a/source/blender/blenlib/BLI_bitmap_draw_2d.h b/source/blender/blenlib/BLI_bitmap_draw_2d.h
index fe890e94f1b..e41439e38d2 100644
--- a/source/blender/blenlib/BLI_bitmap_draw_2d.h
+++ b/source/blender/blenlib/BLI_bitmap_draw_2d.h
@@ -29,6 +29,10 @@ void BLI_bitmap_draw_2d_line_v2v2i(
const int p1[2], const int p2[2],
bool (*callback)(int, int, void *), void *userData);
+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);
+
void BLI_bitmap_draw_2d_poly_v2i_n(
const int xmin, const int ymin, const int xmax, const int ymax,
const int polyXY[][2], const int polyCorners,
diff --git a/source/blender/blenlib/intern/bitmap_draw_2d.c b/source/blender/blenlib/intern/bitmap_draw_2d.c
index fefd4b4587c..c6386cb3080 100644
--- a/source/blender/blenlib/intern/bitmap_draw_2d.c
+++ b/source/blender/blenlib/intern/bitmap_draw_2d.c
@@ -42,7 +42,8 @@
#include "BLI_strict_flags.h"
/* -------------------------------------------------------------------- */
-/* Draw Line */
+/** \name Draw Line
+ * \{ */
/**
* Plot a line from \a p1 to \a p2 (inclusive).
@@ -119,9 +120,186 @@ void BLI_bitmap_draw_2d_line_v2v2i(
}
}
+/** \} */
/* -------------------------------------------------------------------- */
-/* Draw Filled Polygon */
+/** \name Draw Filled Triangle
+ * \{ */
+
+/**
+ * Fill a triangle
+ *
+ * Standard algorithm,
+ * See: http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html
+ *
+ * Changes to the basic implementation:
+ *
+ * - Reuse slope calculation when drawing the second triangle.
+ * - Don't calculate the 4th point at all for the triangle split.
+ * - Order line drawing from left to right (minor detail).
+ * - 1-pixel offsets are applied so adjacent triangles don't overlap.
+ *
+ * This is not clipped, a clipped version can be added if needed.
+ */
+
+/* Macros could be moved to a shared location. */
+#define ORDERED_SWAP(ty, a, b) \
+ if (a > b) { SWAP(ty, a, b); } ((void)0)
+
+#define ORDERED_SWAP_BY(ty, a, b, by) \
+ if ((a by) > (b by)) { SWAP(ty, a, b); } ((void)0)
+
+#define ORDER_VARS2(ty, a, b) \
+ { ORDERED_SWAP(ty, a, b); } ((void)0)
+
+#define ORDER_VARS3_BY(ty, a, b, c, by) \
+ { \
+ ORDERED_SWAP_BY(ty, b, c, by); \
+ ORDERED_SWAP_BY(ty, a, c, by); \
+ ORDERED_SWAP_BY(ty, a, b, by); \
+ } ((void)0)
+
+static float inv_slope(const int a[2], const int b[2])
+{
+ return ((float)(a[0] - b[0]) /
+ (float)(a[1] - b[1]));
+}
+
+/**
+ * <pre>
+ * *---*
+ * \ /
+ * *
+ * </pre>
+ */
+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)
+{
+ float cur_x1 = (float)p[0];
+ float cur_x2 = cur_x1;
+ /* start-end inclusive */
+ const int min_y = p[1];
+ const int max_y_end = max_y + 1;
+ for (int scanline_y = min_y; scanline_y != max_y_end; scanline_y += 1) {
+ callback((int)cur_x1, 1 + (int)cur_x2, scanline_y, user_data);
+ cur_x1 += inv_slope1;
+ cur_x2 += inv_slope2;
+ }
+}
+
+/**
+ * <pre>
+ * *
+ * / \
+ * *---*
+ * </pre>
+ */
+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)
+{
+ float cur_x1 = (float)p[0];
+ float cur_x2 = cur_x1;
+ /* start-end inclusive */
+ const int max_y = p[1];
+ const int min_y_end = min_y - 1;
+ for (int scanline_y = max_y; scanline_y != min_y_end; scanline_y -= 1) {
+ callback((int)cur_x1, 1 + (int)cur_x2, scanline_y, user_data);
+ cur_x1 -= inv_slope1;
+ cur_x2 -= inv_slope2;
+ }
+}
+
+/**
+ * \note Unclipped (clipped version can be added if needed).
+ */
+void BLI_bitmap_draw_2d_tri_v2i(
+ /* all 2d */
+ 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)
+{
+ /* At first sort the three vertices by y-coordinate ascending so p1 is the top-most vertice */
+ ORDER_VARS3_BY(const int *, p1, p2, p3, [1]);
+
+ BLI_assert(p1[1] <= p2[1] && p2[1] <= p3[1]);
+
+ /* Check for trivial case of bottom-flat triangle. */
+ if (p2[1] == p3[1]) {
+ float inv_slope1 = inv_slope(p2, p1);
+ float inv_slope2 = inv_slope(p3, p1);
+ ORDER_VARS2(float, inv_slope1, inv_slope2);
+ BLI_assert(inv_slope1 <= inv_slope2);
+ draw_tri_flat_max(
+ p1, p2[1],
+ inv_slope1, inv_slope2,
+ callback, user_data);
+ }
+ else if (p1[1] == p2[1]) {
+ /* Check for trivial case of top-flat triangle. */
+ float inv_slope1 = inv_slope(p3, p1);
+ float inv_slope2 = inv_slope(p3, p2);
+ ORDER_VARS2(float, inv_slope2, inv_slope1);
+ BLI_assert(inv_slope1 >= inv_slope2);
+ draw_tri_flat_min(
+ p3, p2[1] + 1, /* avoid overlap */
+ inv_slope1, inv_slope2,
+ callback, user_data);
+ }
+ else {
+ /* General case - split the triangle in a top-flat and bottom-flat one. */
+ const float inv_slope_p21 = inv_slope(p2, p1);
+ const float inv_slope_p31 = inv_slope(p3, p1);
+ const float inv_slope_p32 = inv_slope(p3, p2);
+
+ float inv_slope1_max, inv_slope2_max;
+ float inv_slope2_min, inv_slope1_min;
+
+ if (inv_slope_p21 < inv_slope_p31) {
+ inv_slope1_max = inv_slope_p21;
+ inv_slope2_max = inv_slope_p31;
+ inv_slope2_min = inv_slope_p31;
+ inv_slope1_min = inv_slope_p32;
+ }
+ else {
+ inv_slope1_max = inv_slope_p31;
+ inv_slope2_max = inv_slope_p21;
+ inv_slope2_min = inv_slope_p32;
+ inv_slope1_min = inv_slope_p31;
+ }
+
+ draw_tri_flat_max(
+ p1, p2[1],
+ inv_slope1_max, inv_slope2_max,
+ callback, user_data);
+ draw_tri_flat_min(
+ p3, p2[1] + 1, /* avoid overlap */
+ inv_slope1_min, inv_slope2_min,
+ callback, user_data);
+ }
+}
+
+#undef ORDERED_SWAP
+#undef ORDERED_SWAP_BY
+#undef ORDER_VARS2
+#undef ORDER_VARS3_BY
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw Filled Polygon
+ * \{ */
/* sort edge-segments on y, then x axis */
static int draw_poly_v2i_n__span_y_sort(const void *a_p, const void *b_p, void *verts_p)
@@ -334,3 +512,5 @@ void BLI_bitmap_draw_2d_poly_v2i_n(
MEM_freeN(span_y);
MEM_freeN(node_x);
}
+
+/** \} */