1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
(ns glimmer-gfx.raster
"Software rasterizer over an int-array of 0xRRGGBB. Nothing here knows what
a window is -- and nothing here calls into Java. aget/aset/int-array/abs are
all clojure.core; the ^ints/^long hints are hints, not calls."
(:require [clojure.string :as str]))
(defrecord Buf [^ints px ^long w ^long h])
(defn buf [w h] (->Buf (int-array (* w h)) w h))
(defn clear [{:keys [^ints px]} ^long c]
(let [c (int c)] (dotimes [i (alength px)] (aset px i c))))
(defn px!
[{:keys [^ints px ^long w ^long h]} ^long x ^long y ^long c]
(when (and (>= x 0) (< x w) (>= y 0) (< y h))
(aset px (unchecked-add (unchecked-multiply y w) x) (int c))))
(defn rect!
"Filled, clipped."
[{:keys [^ints px ^long w ^long h]} x0 y0 rw rh c]
(let [x1 (min w (+ x0 rw)) y1 (min h (+ y0 rh))
c (int c)]
(loop [y (max 0 y0)]
(when (< y y1)
(let [row (unchecked-multiply y w)]
(loop [x (max 0 x0)]
(when (< x x1)
(aset px (unchecked-add row x) c)
(recur (inc x)))))
(recur (inc y))))))
(defn line!
"Bresenham. ponytail: no AA -- add Wu's if the jaggies bother you."
[b x0 y0 x1 y1 c]
(let [dx (abs (- x1 x0)) dy (- (abs (- y1 y0)))
sx (if (< x0 x1) 1 -1) sy (if (< y0 y1) 1 -1)]
(loop [x x0 y y0 err (+ dx dy)]
(px! b x y c)
(when-not (and (= x x1) (= y y1))
(let [e2 (* 2 err)]
(recur (if (>= e2 dy) (+ x sx) x)
(if (<= e2 dx) (+ y sy) y)
(cond-> err (>= e2 dy) (+ dy) (<= e2 dx) (+ dx))))))))
;; ------------------------------------------------------------------ 3x5 font
;; Authored as strings so a glyph is readable and fixable in place.
(def ^:private glyphs
(->> ["A.#.|#.#|###|#.#|#.#" "B##.|#.#|##.|#.#|##." "C.##|#..|#..|#..|.##"
"D##.|#.#|#.#|#.#|##." "E###|#..|##.|#..|###" "F###|#..|##.|#..|#.."
"G.##|#..|#.#|#.#|.##" "H#.#|#.#|###|#.#|#.#" "I###|.#.|.#.|.#.|###"
"J..#|..#|..#|#.#|.#." "K#.#|#.#|##.|#.#|#.#" "L#..|#..|#..|#..|###"
"M#.#|###|###|#.#|#.#" "N##.|#.#|#.#|#.#|#.#" "O.#.|#.#|#.#|#.#|.#."
"P##.|#.#|##.|#..|#.." "Q.#.|#.#|#.#|###|.##" "R##.|#.#|##.|#.#|#.#"
"S.##|#..|.#.|..#|##." "T###|.#.|.#.|.#.|.#." "U#.#|#.#|#.#|#.#|.##"
"V#.#|#.#|#.#|#.#|.#." "W#.#|#.#|###|###|#.#" "X#.#|#.#|.#.|#.#|#.#"
"Y#.#|#.#|.#.|.#.|.#." "Z###|..#|.#.|#..|###"
"0###|#.#|#.#|#.#|###" "1.#.|##.|.#.|.#.|###" "2##.|..#|.#.|#..|###"
"3##.|..#|.#.|..#|##." "4#.#|#.#|###|..#|..#" "5###|#..|##.|..#|##."
"6.##|#..|###|#.#|###" "7###|..#|.#.|.#.|.#." "8###|#.#|###|#.#|###"
"9###|#.#|###|..#|##."
" ...|...|...|...|..." "....|...|...|...|.#." ":...|.#.|...|.#.|..."
"-...|...|###|...|..." "/..#|..#|.#.|#..|#.." "%#.#|..#|.#.|#..|#.#"
"(.#.|#..|#..|#..|.#." ")#..|.#.|.#.|.#.|#.."]
(map (fn [s] [(first s) (->> (subs s 1) (remove #{\|}) (partition 3)
(mapv str/join))]))
(into {})))
(def ^:const glyph-w 4) ; 3px + 1px gap
(defn text!
"Draws s at (x,y), scaled. Unknown chars render blank."
([b x y s c] (text! b x y s c 2))
([b x y s c k]
(let [s (str/upper-case (str s))]
(dotimes [i (count s)]
(when-let [g (glyphs (nth s i))]
(dotimes [row 5]
(dotimes [col 3]
(when (= \# (nth (g row) col))
(rect! b (+ x (* k (+ (* i glyph-w) col))) (+ y (* k row)) k k c)))))))))
(defn text-w ^long [s ^long k] (* k glyph-w (count s)))
|