altos: Add bitmap drawing code
[fw/altos] / src / draw / ao_blt.c
diff --git a/src/draw/ao_blt.c b/src/draw/ao_blt.c
new file mode 100644 (file)
index 0000000..e3f4522
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * 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 O 0
+#define I AO_ALLONES
+
+struct ao_merge_rop {
+       uint32_t        ca1, cx1, ca2, cx2;
+};
+
+const struct ao_merge_rop ao_merge_rop[16] = {
+    {O, O, O, O},               /* clear         0x0         0 */
+    {I, O, O, O},               /* and           0x1         src AND dst */
+    {I, O, I, O},               /* andReverse    0x2         src AND NOT dst */
+    {O, O, I, O},               /* copy          0x3         src */
+    {I, I, O, O},               /* andInverted   0x4         NOT src AND dst */
+    {O, I, O, O},               /* noop          0x5         dst */
+    {O, I, I, O},               /* xor           0x6         src XOR dst */
+    {I, I, I, O},               /* or            0x7         src OR dst */
+    {I, I, I, I},               /* nor           0x8         NOT src AND NOT dst */
+    {O, I, I, I},               /* equiv         0x9         NOT src XOR dst */
+    {O, I, O, I},               /* invert        0xa         NOT dst */
+    {I, I, O, I},               /* orReverse     0xb         src OR NOT dst */
+    {O, O, I, I},               /* copyInverted  0xc         NOT src */
+    {I, O, I, I},               /* orInverted    0xd         NOT src OR dst */
+    {I, O, O, I},               /* nand          0xe         NOT src OR NOT dst */
+    {O, O, O, I},               /* set           0xf         1 */
+};
+
+#define ao_do_merge_rop(src, dst) \
+    (((dst) & (((src) & _ca1) ^ _cx1)) ^ (((src) & _ca2) ^ _cx2))
+
+#define ao_do_dst_invarient_merge_rop(src)     (((src) & _ca2) ^ _cx2)
+
+#define ao_do_mask_merge_rop(src, dst, mask) \
+    (((dst) & ((((src) & _ca1) ^ _cx1) | ~(mask))) ^ ((((src) & _ca2) ^ _cx2) & (mask)))
+
+#define ao_dst_invarient_merge_rop()   (_ca1 == 0 && _cx1 == 0)
+
+void
+ao_blt(uint32_t                *src_line,
+       int16_t         src_stride,
+       int16_t         src_x,
+       uint32_t                *dst_line,
+       int16_t         dst_stride,
+       int16_t         dst_x,
+       int16_t         width,
+       int16_t         height,
+       uint8_t         rop,
+       uint8_t         reverse,
+       uint8_t         upsidedown)
+{
+       uint32_t        *src, *dst;
+       uint32_t        _ca1, _cx1, _ca2, _cx2;
+       uint8_t         dst_invarient;
+       uint32_t        startmask, endmask;
+       int16_t         nmiddle, n;
+       uint32_t        bits1, bits;
+       int16_t         left_shift, right_shift;
+
+       _ca1 = ao_merge_rop[rop].ca1;
+       _cx1 = ao_merge_rop[rop].cx1;
+       _ca2 = ao_merge_rop[rop].ca2;
+       _cx2 = ao_merge_rop[rop].cx2;
+       dst_invarient = ao_dst_invarient_merge_rop();
+
+       if (upsidedown) {
+               src_line += (height - 1) * src_stride;
+               dst_line += (height - 1) * dst_stride;
+               src_stride = -src_stride;
+               dst_stride = -dst_stride;
+       }
+
+       ao_mask_bits(dst_x, width, startmask, nmiddle, endmask);
+       if (reverse) {
+               src_line += ((src_x + width - 1) >> AO_SHIFT) + 1;
+               dst_line += ((dst_x + width - 1) >> AO_SHIFT) + 1;
+               src_x = (src_x + width - 1) & AO_MASK;
+               dst_x = (dst_x + width - 1) & AO_MASK;
+       } else {
+               src_line += src_x >> AO_SHIFT;
+               dst_line += dst_x >> AO_SHIFT;
+               src_x &= AO_MASK;
+               dst_x &= AO_MASK;
+       }
+       if (src_x == dst_x) {
+               while (height--) {
+                       src = src_line;
+                       src_line += src_stride;
+                       dst = dst_line;
+                       dst_line += dst_stride;
+                       if (reverse) {
+                               if (endmask) {
+                                       bits = *--src;
+                                       --dst;
+                                       *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
+                               }
+                               n = nmiddle;
+                               if (dst_invarient) {
+                                       while (n--)
+                                               *--dst = ao_do_dst_invarient_merge_rop(*--src);
+                               }
+                               else {
+                                       while (n--) {
+                                               bits = *--src;
+                                               --dst;
+                                               *dst = ao_do_merge_rop(bits, *dst);
+                                       }
+                               }
+                               if (startmask) {
+                                       bits = *--src;
+                                       --dst;
+                                       *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
+                               }
+                       }
+                       else {
+                               if (startmask) {
+                                       bits = *src++;
+                                       *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
+                                       dst++;
+                               }
+                               n = nmiddle;
+                               if (dst_invarient) {
+                                       while (n--)
+                                               *dst++ = ao_do_dst_invarient_merge_rop(*src++);
+                               }
+                               else {
+                                       while (n--) {
+                                               bits = *src++;
+                                               *dst = ao_do_merge_rop(bits, *dst);
+                                               dst++;
+                                       }
+                               }
+                               if (endmask) {
+                                       bits = *src;
+                                       *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
+                               }
+                       }
+               }
+       } else {
+               if (src_x > dst_x) {
+                       left_shift = src_x - dst_x;
+                       right_shift = AO_UNIT - left_shift;
+               } else {
+                       right_shift = dst_x - src_x;
+                       left_shift = AO_UNIT - right_shift;
+               }
+               while (height--) {
+                       src = src_line;
+                       src_line += src_stride;
+                       dst = dst_line;
+                       dst_line += dst_stride;
+
+                       bits1 = 0;
+                       if (reverse) {
+                               if (src_x < dst_x)
+                                       bits1 = *--src;
+                               if (endmask) {
+                                       bits = ao_right(bits1, right_shift);
+                                       if (ao_right(endmask, left_shift)) {
+                                               bits1 = *--src;
+                                               bits |= ao_left(bits1, left_shift);
+                                       }
+                                       --dst;
+                                       *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
+                               }
+                               n = nmiddle;
+                               if (dst_invarient) {
+                                       while (n--) {
+                                               bits = ao_right(bits1, right_shift);
+                                               bits1 = *--src;
+                                               bits |= ao_left(bits1, left_shift);
+                                               --dst;
+                                               *dst = ao_do_dst_invarient_merge_rop(bits);
+                                       }
+                               } else {
+                                       while (n--) {
+                                               bits = ao_right(bits1, right_shift);
+                                               bits1 = *--src;
+                                               bits |= ao_left(bits1, left_shift);
+                                               --dst;
+                                               *dst = ao_do_merge_rop(bits, *dst);
+                                       }
+                               }
+                               if (startmask) {
+                                       bits = ao_right(bits1, right_shift);
+                                       if (ao_right(startmask, left_shift)) {
+                                               bits1 = *--src;
+                                               bits |= ao_left(bits1, left_shift);
+                                       }
+                                       --dst;
+                                       *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
+                               }
+                       }
+                       else {
+                               if (src_x > dst_x)
+                                       bits1 = *src++;
+                               if (startmask) {
+                                       bits = ao_left(bits1, left_shift);
+                                       if (ao_left(startmask, right_shift)) {
+                                               bits1 = *src++;
+                                               bits |= ao_right(bits1, right_shift);
+                                       }
+                                       *dst = ao_do_mask_merge_rop(bits, *dst, startmask);
+                                       dst++;
+                               }
+                               n = nmiddle;
+                               if (dst_invarient) {
+                                       while (n--) {
+                                               bits = ao_left(bits1, left_shift);
+                                               bits1 = *src++;
+                                               bits |= ao_right(bits1, right_shift);
+                                               *dst = ao_do_dst_invarient_merge_rop(bits);
+                                               dst++;
+                                       }
+                               }
+                               else {
+                                       while (n--) {
+                                               bits = ao_left(bits1, left_shift);
+                                               bits1 = *src++;
+                                               bits |= ao_right(bits1, right_shift);
+                                               *dst = ao_do_merge_rop(bits, *dst);
+                                               dst++;
+                                       }
+                               }
+                               if (endmask) {
+                                       bits = ao_left(bits1, left_shift);
+                                       if (ao_left(endmask, right_shift)) {
+                                               bits1 = *src;
+                                               bits |= ao_right(bits1, right_shift);
+                                       }
+                                       *dst = ao_do_mask_merge_rop(bits, *dst, endmask);
+                               }
+                       }
+               }
+       }
+}
+
+void
+ao_solid(uint32_t      and,
+        uint32_t       xor,
+        uint32_t       *dst,
+        int16_t        dst_stride,
+        int16_t        dst_x,
+        int16_t        width,
+        int16_t        height)
+{
+       uint32_t        startmask, endmask;
+       int16_t         nmiddle;
+       int16_t         n;
+
+       dst += dst_x >> AO_SHIFT;
+       dst_x &= AO_MASK;
+
+       ao_mask_bits(dst_x, width, startmask, nmiddle, endmask);
+
+       if (startmask)
+               dst_stride--;
+
+       dst_stride -= nmiddle;
+       while (height--) {
+               if (startmask) {
+                       *dst = ao_do_mask_rrop(*dst, and, xor, startmask);
+                       dst++;
+               }
+               n = nmiddle;
+               if (!and)
+                       while (n--)
+                               *dst++ = xor;
+               else
+                       while (n--) {
+                               *dst = ao_do_rrop(*dst, and, xor);
+                               dst++;
+                       }
+               if (endmask)
+                       *dst = ao_do_mask_rrop(*dst, and, xor, endmask);
+               dst += dst_stride;
+       }
+}