altos/draw: Add a reasonable API for drawing, add lines.
[fw/altos] / src / draw / ao_line.c
diff --git a/src/draw/ao_line.c b/src/draw/ao_line.c
new file mode 100644 (file)
index 0000000..b427892
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_draw.h"
+#include "ao_draw_int.h"
+
+#define ao_mask(x,w)   (ao_right(AO_ALLONES,(x) & AO_MASK) & \
+                        ao_left(AO_ALLONES,(FB_UNIT - ((x)+(w))) & AO_MASK))
+
+
+/* out of clip region codes */
+#define OUT_LEFT 0x08
+#define OUT_RIGHT 0x04
+#define OUT_ABOVE 0x02
+#define OUT_BELOW 0x01
+
+/* major axis for bresenham's line */
+#define X_AXIS 0
+#define Y_AXIS 1
+
+static void
+ao_bres(const struct ao_bitmap *dst_bitmap,
+       int16_t         signdx,
+       int16_t         signdy,
+       int16_t         axis,
+       int16_t         x1,
+       int16_t         y1,
+       int16_t         e,
+       int16_t         e1,
+       int16_t         e3,
+       int16_t         len,
+       uint32_t        and,
+       uint32_t        xor)
+{
+       int16_t         stride = dst_bitmap->stride;
+       uint32_t        *dst = dst_bitmap->base;
+       uint32_t        mask0, mask;
+
+       mask0 = 1;
+       if (signdx < 0)
+               mask0 = ao_right(1, AO_UNIT - 1);
+
+       if (signdy < 0)
+               stride = -stride;
+
+       dst = dst + y1 * stride + (x1 >> AO_SHIFT);
+       mask = ao_right(1, x1 & AO_MASK);
+
+       while (len--) {
+               /* clip each point */
+
+               if (0 <= x1 && x1 < dst_bitmap->width &&
+                   0 <= y1 && y1 < dst_bitmap->height)
+                       *dst = ao_do_mask_rrop(*dst, and, xor, mask);
+
+               if (axis == X_AXIS) {
+                       if (signdx < 0)
+                               mask = ao_left(mask, 1);
+                       else
+                               mask = ao_right(mask, 1);
+                       if (!mask) {
+                               dst += signdx;
+                               mask = mask0;
+                       }
+                       x1 += signdx;
+                       e += e1;
+                       if (e >= 0) {
+                               dst += stride;
+                               e += e3;
+                               y1 += signdy;
+                       }
+               } else {
+                       dst += stride;
+                       e += e1;
+                       y1 += signdy;
+                       if (e >= 0) {
+                               if (signdx < 0)
+                                       mask = ao_left(mask, 1);
+                               else
+                                       mask = ao_right(mask, 1);
+                               if (!mask) {
+                                       dst += signdx;
+                                       mask = mask0;
+                               }
+                               e += e3;
+                               x1 += signdx;
+                       }
+               }
+       }
+}
+
+
+#define bound(val,max) do {                    \
+               if (val < 0) {                  \
+                       val = 0;                \
+               }                               \
+               if (val > max) {                \
+                       val = max;              \
+               }                               \
+       } while (0)
+
+void
+ao_line(const struct ao_bitmap *dst,
+       int16_t                 x1,
+       int16_t                 y1,
+       int16_t                 x2,
+       int16_t                 y2,
+       uint32_t                fill,
+       uint8_t                 rop)
+{
+       int16_t adx, ady;
+       int16_t e, e1, e2, e3;
+       int16_t signdx = 1, signdy = 1;
+       int16_t axis;
+       int16_t len;
+
+       if ((adx = x2 - x1) < 0) {
+               adx = -adx;
+               signdx = -1;
+       }
+       if ((ady = y2 - y1) < 0) {
+               ady = -ady;
+               signdy = -1;
+       }
+
+       if (adx > ady) {
+               axis = X_AXIS;
+               e1 = ady << 1;
+               e2 = e1 - (adx << 1);
+               e = e1 - adx;
+               len = adx;
+       } else {
+               axis = Y_AXIS;
+               e1 = adx << 1;
+               e2 = e1 - (ady << 1);
+               e = e1 - ady;
+               len = ady;
+       }
+
+       e3 = e2 - e1;
+       e = e - e1;
+
+       ao_bres(dst,
+               signdx,
+               signdy,
+               axis,
+               x1,
+               y1,
+               e, e1, e3, len,
+               ao_and(rop, fill),
+               ao_xor(rop, fill));
+}