https://github.com/akkartik/mu/blob/main/apps/colors.mu

      1 # Return colors 'near' a given r/g/b value (expressed in hex)
      2 # If we did this rigorously we'd need to implement cosines. So we won't.
      3 #
      4 # To build:
      5 #   $ ./translate apps/colors.mu
      6 #
      7 # Example session:
      8 #   $ qemu-system-i386 code.img
      9 #   Enter 3 hex bytes for r, g, b (lowercase; no 0x prefix) separated by a single space> aa 0 aa
     10 #   5
     11 # This means only color 5 in the default palette is similar to #aa00aa.
     12
     13 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
     14   var in-storage: (stream byte 0x10)
     15   var in/esi: (addr stream byte) <- address in-storage
     16   {
     17     # print prompt
     18     var x/eax: int <- draw-text-rightward screen, "Enter 3 hex bytes for r, g, b (lowercase; no 0x prefix) separated by a single space> ", 0x10/x, 0x80/xmax, 0x28/y, 3/fg/cyan, 0/bg
     19     # read line from keyboard
     20     clear-stream in
     21     {
     22       draw-cursor screen, 0x20/space
     23       var key/eax: byte <- read-key keyboard
     24       compare key, 0xa/newline
     25       break-if-=
     26       compare key, 0
     27       loop-if-=
     28       var key2/eax: int <- copy key
     29       append-byte in, key2
     30       var g/eax: grapheme <- copy key2
     31       draw-grapheme-at-cursor screen, g, 0xf/fg, 0/bg
     32       move-cursor-right 0
     33       loop
     34     }
     35     clear-screen screen
     36     # parse
     37     var a/ecx: int <- copy 0
     38     var b/edx: int <- copy 0
     39     var c/ebx: int <- copy 0
     40     # a, b, c = r, g, b
     41     a, b, c <- parse in
     42 #?     set-cursor-position screen, 0x10/x, 0x1a/y
     43 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, a, 7/fg, 0/bg
     44 #?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
     45 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, b, 7/fg, 0/bg
     46 #?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
     47 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, c, 7/fg, 0/bg
     48     a, b, c <- hsl a, b, c
     49     # return all colors in the same quadrant in h, s and l
     50     print-nearby-colors screen, a, b, c
     51     # another metric
     52     var color/eax: int <- nearest-color-euclidean-hsl a, b, c
     53     set-cursor-position screen, 0x10/x, 0x26/y
     54     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "nearest (euclidean, h/s/l): ", 0xf/fg, 0/bg
     55     draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, color, 7/fg, 0/bg
     56     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 0xf/fg, 0/bg
     57     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "               ", 0/fg, color
     58     #
     59     loop
     60   }
     61 }
     62
     63 # read exactly 3 words in a single line
     64 # Each word consists of exactly 1 or 2 hex bytes. No hex prefix.
     65 fn parse in: (addr stream byte) -> _/ecx: int, _/edx: int, _/ebx: int {
     66   # read first byte of r
     67   var tmp/eax: byte <- read-byte in
     68   {
     69     var valid?/eax: boolean <- hex-digit? tmp
     70     compare valid?, 0/false
     71     break-if-!=
     72     abort "invalid byte 0 of r"
     73   }
     74   tmp <- fast-hex-digit-value tmp
     75   var r/ecx: int <- copy tmp
     76 #?   set-cursor-position 0/screen, 0x10/x, 0x10/y
     77 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, r, 7/fg, 0/bg
     78   # read second byte of r
     79   tmp <- read-byte in
     80   {
     81     {
     82       var valid?/eax: boolean <- hex-digit? tmp
     83       compare valid?, 0/false
     84     }
     85     break-if-=
     86     r <- shift-left 4
     87     tmp <- fast-hex-digit-value tmp
     88 #?     {
     89 #?       var foo/eax: int <- copy tmp
     90 #?       set-cursor-position 0/screen, 0x10/x, 0x11/y
     91 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
     92 #?     }
     93     r <- add tmp
     94 #?     {
     95 #?       set-cursor-position 0/screen, 0x10/x, 0x12/y
     96 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, r, 7/fg, 0/bg
     97 #?     }
     98     tmp <- read-byte in  # skip space
     99   }
    100   # read first byte of g
    101   var tmp/eax: byte <- read-byte in
    102   {
    103     var valid?/eax: boolean <- hex-digit? tmp
    104     compare valid?, 0/false
    105     break-if-!=
    106     abort "invalid byte 0 of g"
    107   }
    108   tmp <- fast-hex-digit-value tmp
    109   var g/edx: int <- copy tmp
    110 #?   set-cursor-position 0/screen, 0x10/x, 0x13/y
    111 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, g, 7/fg, 0/bg
    112   # read second byte of g
    113   tmp <- read-byte in
    114   {
    115     {
    116       var valid?/eax: boolean <- hex-digit? tmp
    117       compare valid?, 0/false
    118     }
    119     break-if-=
    120     g <- shift-left 4
    121     tmp <- fast-hex-digit-value tmp
    122 #?     {
    123 #?       var foo/eax: int <- copy tmp
    124 #?       set-cursor-position 0/screen, 0x10/x, 0x14/y
    125 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
    126 #?     }
    127     g <- add tmp
    128 #?     {
    129 #?       set-cursor-position 0/screen, 0x10/x, 0x15/y
    130 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, g, 7/fg, 0/bg
    131 #?     }
    132     tmp <- read-byte in  # skip space
    133   }
    134   # read first byte of b
    135   var tmp/eax: byte <- read-byte in
    136   {
    137     var valid?/eax: boolean <- hex-digit? tmp
    138     compare valid?, 0/false
    139     break-if-!=
    140     abort "invalid byte 0 of b"
    141   }
    142   tmp <- fast-hex-digit-value tmp
    143   var b/ebx: int <- copy tmp
    144 #?   set-cursor-position 0/screen, 0x10/x, 0x16/y
    145 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, b, 7/fg, 0/bg
    146   # read second byte of b
    147   {
    148     {
    149       var done?/eax: boolean <- stream-empty? in
    150       compare done?, 0/false
    151     }
    152     break-if-!=
    153     tmp <- read-byte in
    154     {
    155       var valid?/eax: boolean <- hex-digit? tmp
    156       compare valid?, 0/false
    157     }
    158     break-if-=
    159     b <- shift-left 4
    160     tmp <- fast-hex-digit-value tmp
    161 #?     {
    162 #?       var foo/eax: int <- copy tmp
    163 #?       set-cursor-position 0/screen, 0x10/x, 0x17/y
    164 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
    165 #?     }
    166     b <- add tmp
    167 #?     {
    168 #?       set-cursor-position 0/screen, 0x10/x, 0x18/y
    169 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, b, 7/fg, 0/bg
    170 #?     }
    171   }
    172   return r, g, b
    173 }
    174
    175 # no error checking
    176 fn fast-hex-digit-value in: byte -> _/eax: byte {
    177   var result/eax: byte <- copy in
    178   compare result, 0x39
    179   {
    180     break-if->
    181     result <- subtract 0x30/0
    182     return result
    183   }
    184   result <- subtract 0x61/a
    185   result <- add 0xa/10
    186   return result
    187 }
    188
    189 fn print-nearby-colors screen: (addr screen), h: int, s: int, l: int {
    190 #?   set-cursor-position screen, 0x10/x, 0x1c/y
    191 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, h, 7/fg, 0/bg
    192 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
    193 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, s, 7/fg, 0/bg
    194 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
    195 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, l, 7/fg, 0/bg
    196   # save just top 2 bits of each, so that we narrow down to 1/64th of the volume
    197   shift-right h, 6
    198   shift-right s, 6
    199   shift-right l, 6
    200 #?   set-cursor-position screen, 0x10/x, 0x1/y
    201 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, h, 7/fg, 0/bg
    202 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
    203 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, s, 7/fg, 0/bg
    204 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
    205 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, l, 7/fg, 0/bg
    206   var a/ecx: int <- copy 0
    207   var b/edx: int <- copy 0
    208   var c/ebx: int <- copy 0
    209   var color/eax: int <- copy 0
    210   var y/esi: int <- copy 2
    211   {
    212     compare color, 0x100
    213     break-if->=
    214     a, b, c <- color-rgb color
    215     a, b, c <- hsl a, b, c
    216     a <- shift-right 6
    217     b <- shift-right 6
    218     c <- shift-right 6
    219     {
    220       compare a, h
    221       break-if-!=
    222       compare b, s
    223       break-if-!=
    224       compare c, l
    225       break-if-!=
    226       set-cursor-position screen, 0x10/x, y
    227       draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, color, 7/fg, 0/bg
    228       set-cursor-position screen, 0x14/x, y
    229       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
    230       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "               ", 0/fg, color
    231 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
    232 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, a, 7/fg, 0/bg
    233 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
    234 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, b, 7/fg, 0/bg
    235 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
    236 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, c, 7/fg, 0/bg
    237       y <- increment
    238     }
    239     color <- increment
    240     loop
    241   }
    242 }