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