altos/telelco-v2.0: A bit fancier with the drag-mode LED show
[fw/altos] / src / draw / ao_blt.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 O 0
20 #define I AO_ALLONES
21
22 struct ao_merge_rop {
23         uint32_t        ca1, cx1, ca2, cx2;
24 };
25
26 const struct ao_merge_rop ao_merge_rop[16] = {
27     {O, O, O, O},               /* clear         0x0         0 */
28     {I, O, O, O},               /* and           0x1         src AND dst */
29     {I, O, I, O},               /* andReverse    0x2         src AND NOT dst */
30     {O, O, I, O},               /* copy          0x3         src */
31     {I, I, O, O},               /* andInverted   0x4         NOT src AND dst */
32     {O, I, O, O},               /* noop          0x5         dst */
33     {O, I, I, O},               /* xor           0x6         src XOR dst */
34     {I, I, I, O},               /* or            0x7         src OR dst */
35     {I, I, I, I},               /* nor           0x8         NOT src AND NOT dst */
36     {O, I, I, I},               /* equiv         0x9         NOT src XOR dst */
37     {O, I, O, I},               /* invert        0xa         NOT dst */
38     {I, I, O, I},               /* orReverse     0xb         src OR NOT dst */
39     {O, O, I, I},               /* copyInverted  0xc         NOT src */
40     {I, O, I, I},               /* orInverted    0xd         NOT src OR dst */
41     {I, O, O, I},               /* nand          0xe         NOT src OR NOT dst */
42     {O, O, O, I},               /* set           0xf         1 */
43 };
44
45 #define ao_do_merge_rop(src, dst) \
46     (((dst) & (((src) & _ca1) ^ _cx1)) ^ (((src) & _ca2) ^ _cx2))
47
48 #define ao_do_dst_invarient_merge_rop(src)      (((src) & _ca2) ^ _cx2)
49
50 #define ao_do_mask_merge_rop(src, dst, mask) \
51     (((dst) & ((((src) & _ca1) ^ _cx1) | ~(mask))) ^ ((((src) & _ca2) ^ _cx2) & (mask)))
52
53 #define ao_dst_invarient_merge_rop()   (_ca1 == 0 && _cx1 == 0)
54
55 void
56 ao_blt(uint32_t         *src_line,
57        int16_t          src_stride,
58        int16_t          src_x,
59        uint32_t         *dst_line,
60        int16_t          dst_stride,
61        int16_t          dst_x,
62        int16_t          width,
63        int16_t          height,
64        uint8_t          rop,
65        uint8_t          reverse,
66        uint8_t          upsidedown)
67 {
68         uint32_t        *src, *dst;
69         uint32_t        _ca1, _cx1, _ca2, _cx2;
70         uint8_t         dst_invarient;
71         uint32_t        startmask, endmask;
72         int16_t         nmiddle, n;
73         uint32_t        bits1, bits;
74         int16_t         left_shift, right_shift;
75
76         _ca1 = ao_merge_rop[rop].ca1;
77         _cx1 = ao_merge_rop[rop].cx1;
78         _ca2 = ao_merge_rop[rop].ca2;
79         _cx2 = ao_merge_rop[rop].cx2;
80         dst_invarient = ao_dst_invarient_merge_rop();
81
82         if (upsidedown) {
83                 src_line += (height - 1) * src_stride;
84                 dst_line += (height - 1) * dst_stride;
85                 src_stride = -src_stride;
86                 dst_stride = -dst_stride;
87         }
88
89         ao_mask_bits(dst_x, width, startmask, nmiddle, endmask);
90         if (reverse) {
91                 src_line += ((src_x + width - 1) >> AO_SHIFT) + 1;
92                 dst_line += ((dst_x + width - 1) >> AO_SHIFT) + 1;
93                 src_x = (src_x + width - 1) & AO_MASK;
94                 dst_x = (dst_x + width - 1) & AO_MASK;
95         } else {
96                 src_line += src_x >> AO_SHIFT;
97                 dst_line += dst_x >> AO_SHIFT;
98                 src_x &= AO_MASK;
99                 dst_x &= AO_MASK;
100         }
101         if (src_x == dst_x) {
102                 while (height--) {
103                         src = src_line;
104                         src_line += src_stride;
105                         dst = dst_line;
106                         dst_line += dst_stride;
107                         if (reverse) {
108                                 if (endmask) {
109                                         bits = *--src;
110                                         --dst;
111                                         *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
112                                 }
113                                 n = nmiddle;
114                                 if (dst_invarient) {
115                                         while (n--)
116                                                 *--dst = ao_do_dst_invarient_merge_rop(*--src);
117                                 }
118                                 else {
119                                         while (n--) {
120                                                 bits = *--src;
121                                                 --dst;
122                                                 *dst = ao_do_merge_rop(bits, *dst);
123                                         }
124                                 }
125                                 if (startmask) {
126                                         bits = *--src;
127                                         --dst;
128                                         *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
129                                 }
130                         }
131                         else {
132                                 if (startmask) {
133                                         bits = *src++;
134                                         *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
135                                         dst++;
136                                 }
137                                 n = nmiddle;
138                                 if (dst_invarient) {
139                                         while (n--)
140                                                 *dst++ = ao_do_dst_invarient_merge_rop(*src++);
141                                 }
142                                 else {
143                                         while (n--) {
144                                                 bits = *src++;
145                                                 *dst = ao_do_merge_rop(bits, *dst);
146                                                 dst++;
147                                         }
148                                 }
149                                 if (endmask) {
150                                         bits = *src;
151                                         *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
152                                 }
153                         }
154                 }
155         } else {
156                 if (src_x > dst_x) {
157                         left_shift = src_x - dst_x;
158                         right_shift = AO_UNIT - left_shift;
159                 } else {
160                         right_shift = dst_x - src_x;
161                         left_shift = AO_UNIT - right_shift;
162                 }
163                 while (height--) {
164                         src = src_line;
165                         src_line += src_stride;
166                         dst = dst_line;
167                         dst_line += dst_stride;
168
169                         bits1 = 0;
170                         if (reverse) {
171                                 if (src_x < dst_x)
172                                         bits1 = *--src;
173                                 if (endmask) {
174                                         bits = ao_right(bits1, right_shift);
175                                         if (ao_right(endmask, left_shift)) {
176                                                 bits1 = *--src;
177                                                 bits |= ao_left(bits1, left_shift);
178                                         }
179                                         --dst;
180                                         *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
181                                 }
182                                 n = nmiddle;
183                                 if (dst_invarient) {
184                                         while (n--) {
185                                                 bits = ao_right(bits1, right_shift);
186                                                 bits1 = *--src;
187                                                 bits |= ao_left(bits1, left_shift);
188                                                 --dst;
189                                                 *dst = ao_do_dst_invarient_merge_rop(bits);
190                                         }
191                                 } else {
192                                         while (n--) {
193                                                 bits = ao_right(bits1, right_shift);
194                                                 bits1 = *--src;
195                                                 bits |= ao_left(bits1, left_shift);
196                                                 --dst;
197                                                 *dst = ao_do_merge_rop(bits, *dst);
198                                         }
199                                 }
200                                 if (startmask) {
201                                         bits = ao_right(bits1, right_shift);
202                                         if (ao_right(startmask, left_shift)) {
203                                                 bits1 = *--src;
204                                                 bits |= ao_left(bits1, left_shift);
205                                         }
206                                         --dst;
207                                         *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
208                                 }
209                         }
210                         else {
211                                 if (src_x > dst_x)
212                                         bits1 = *src++;
213                                 if (startmask) {
214                                         bits = ao_left(bits1, left_shift);
215                                         if (ao_left(startmask, right_shift)) {
216                                                 bits1 = *src++;
217                                                 bits |= ao_right(bits1, right_shift);
218                                         }
219                                         *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
220                                         dst++;
221                                 }
222                                 n = nmiddle;
223                                 if (dst_invarient) {
224                                         while (n--) {
225                                                 bits = ao_left(bits1, left_shift);
226                                                 bits1 = *src++;
227                                                 bits |= ao_right(bits1, right_shift);
228                                                 *dst = ao_do_dst_invarient_merge_rop(bits);
229                                                 dst++;
230                                         }
231                                 }
232                                 else {
233                                         while (n--) {
234                                                 bits = ao_left(bits1, left_shift);
235                                                 bits1 = *src++;
236                                                 bits |= ao_right(bits1, right_shift);
237                                                 *dst = ao_do_merge_rop(bits, *dst);
238                                                 dst++;
239                                         }
240                                 }
241                                 if (endmask) {
242                                         bits = ao_left(bits1, left_shift);
243                                         if (ao_left(endmask, right_shift)) {
244                                                 bits1 = *src;
245                                                 bits |= ao_right(bits1, right_shift);
246                                         }
247                                         *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
248                                 }
249                         }
250                 }
251         }
252 }
253
254 void
255 ao_solid(uint32_t       and,
256          uint32_t       xor,
257          uint32_t       *dst,
258          int16_t        dst_stride,
259          int16_t        dst_x,
260          int16_t        width,
261          int16_t        height)
262 {
263         uint32_t        startmask, endmask;
264         int16_t         nmiddle;
265         int16_t         n;
266
267         dst += dst_x >> AO_SHIFT;
268         dst_x &= AO_MASK;
269
270         ao_mask_bits(dst_x, width, startmask, nmiddle, endmask);
271
272         if (startmask)
273                 dst_stride--;
274
275         dst_stride -= nmiddle;
276         while (height--) {
277                 if (startmask) {
278                         *dst = ao_do_mask_rrop(*dst, and, xor, startmask);
279                         dst++;
280                 }
281                 n = nmiddle;
282                 if (!and)
283                         while (n--)
284                                 *dst++ = xor;
285                 else
286                         while (n--) {
287                                 *dst = ao_do_rrop(*dst, and, xor);
288                                 dst++;
289                         }
290                 if (endmask)
291                         *dst = ao_do_mask_rrop(*dst, and, xor, endmask);
292                 dst += dst_stride;
293         }
294 }