altos/draw: Add a reasonable API for drawing, add lines.
[fw/altos] / src / draw / ao_line.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #include "ao.h"
16 #include "ao_draw.h"
17 #include "ao_draw_int.h"
18
19 #define ao_mask(x,w)    (ao_right(AO_ALLONES,(x) & AO_MASK) & \
20                          ao_left(AO_ALLONES,(FB_UNIT - ((x)+(w))) & AO_MASK))
21
22
23 /* out of clip region codes */
24 #define OUT_LEFT 0x08
25 #define OUT_RIGHT 0x04
26 #define OUT_ABOVE 0x02
27 #define OUT_BELOW 0x01
28
29 /* major axis for bresenham's line */
30 #define X_AXIS  0
31 #define Y_AXIS  1
32
33 static void
34 ao_bres(const struct ao_bitmap  *dst_bitmap,
35         int16_t         signdx,
36         int16_t         signdy,
37         int16_t         axis,
38         int16_t         x1,
39         int16_t         y1,
40         int16_t         e,
41         int16_t         e1,
42         int16_t         e3,
43         int16_t         len,
44         uint32_t        and,
45         uint32_t        xor)
46 {
47         int16_t         stride = dst_bitmap->stride;
48         uint32_t        *dst = dst_bitmap->base;
49         uint32_t        mask0, mask;
50
51         mask0 = 1;
52         if (signdx < 0)
53                 mask0 = ao_right(1, AO_UNIT - 1);
54
55         if (signdy < 0)
56                 stride = -stride;
57
58         dst = dst + y1 * stride + (x1 >> AO_SHIFT);
59         mask = ao_right(1, x1 & AO_MASK);
60
61         while (len--) {
62                 /* clip each point */
63
64                 if (0 <= x1 && x1 < dst_bitmap->width &&
65                     0 <= y1 && y1 < dst_bitmap->height)
66                         *dst = ao_do_mask_rrop(*dst, and, xor, mask);
67
68                 if (axis == X_AXIS) {
69                         if (signdx < 0)
70                                 mask = ao_left(mask, 1);
71                         else
72                                 mask = ao_right(mask, 1);
73                         if (!mask) {
74                                 dst += signdx;
75                                 mask = mask0;
76                         }
77                         x1 += signdx;
78                         e += e1;
79                         if (e >= 0) {
80                                 dst += stride;
81                                 e += e3;
82                                 y1 += signdy;
83                         }
84                 } else {
85                         dst += stride;
86                         e += e1;
87                         y1 += signdy;
88                         if (e >= 0) {
89                                 if (signdx < 0)
90                                         mask = ao_left(mask, 1);
91                                 else
92                                         mask = ao_right(mask, 1);
93                                 if (!mask) {
94                                         dst += signdx;
95                                         mask = mask0;
96                                 }
97                                 e += e3;
98                                 x1 += signdx;
99                         }
100                 }
101         }
102 }
103
104
105 #define bound(val,max) do {                     \
106                 if (val < 0) {                  \
107                         val = 0;                \
108                 }                               \
109                 if (val > max) {                \
110                         val = max;              \
111                 }                               \
112         } while (0)
113
114 void
115 ao_line(const struct ao_bitmap  *dst,
116         int16_t                 x1,
117         int16_t                 y1,
118         int16_t                 x2,
119         int16_t                 y2,
120         uint32_t                fill,
121         uint8_t                 rop)
122 {
123         int16_t adx, ady;
124         int16_t e, e1, e2, e3;
125         int16_t signdx = 1, signdy = 1;
126         int16_t axis;
127         int16_t len;
128
129         if ((adx = x2 - x1) < 0) {
130                 adx = -adx;
131                 signdx = -1;
132         }
133         if ((ady = y2 - y1) < 0) {
134                 ady = -ady;
135                 signdy = -1;
136         }
137
138         if (adx > ady) {
139                 axis = X_AXIS;
140                 e1 = ady << 1;
141                 e2 = e1 - (adx << 1);
142                 e = e1 - adx;
143                 len = adx;
144         } else {
145                 axis = Y_AXIS;
146                 e1 = adx << 1;
147                 e2 = e1 - (ady << 1);
148                 e = e1 - ady;
149                 len = ady;
150         }
151
152         e3 = e2 - e1;
153         e = e - e1;
154
155         ao_bres(dst,
156                 signdx,
157                 signdy,
158                 axis,
159                 x1,
160                 y1,
161                 e, e1, e3, len,
162                 ao_and(rop, fill),
163                 ao_xor(rop, fill));
164 }