about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-07-10 10:51:39 -0700
committerKartik K. Agaram <vc@akkartik.com>2021-07-10 10:51:39 -0700
commit5f8a8c11a25cf3de2e98c0bfda4e0cbdb8b8da54 (patch)
treeafac3c65fb5cb105bedf0a94a423a40a5fcc0946
parent2a0f6cbb6ff9fd3f76b335621e852d49cbc0e94f (diff)
downloadmu-5f8a8c11a25cf3de2e98c0bfda4e0cbdb8b8da54.tar.gz
dithering greyscale using black/white
Starting to look right.
-rw-r--r--7f.pgm7
-rw-r--r--80.pgm7
-rw-r--r--c0.pgm7
-rw-r--r--img.mu289
-rw-r--r--white.pgm7
5 files changed, 293 insertions, 24 deletions
diff --git a/7f.pgm b/7f.pgm
new file mode 100644
index 00000000..34bc9d70
--- /dev/null
+++ b/7f.pgm
@@ -0,0 +1,7 @@
+P2
+4 4
+255
+127 127 127 127
+127 127 127 127
+127 127 127 127
+127 127 127 127
diff --git a/80.pgm b/80.pgm
new file mode 100644
index 00000000..4495f46f
--- /dev/null
+++ b/80.pgm
@@ -0,0 +1,7 @@
+P2
+4 4
+255
+128 128 128 128
+128 128 128 128
+128 128 128 128
+128 128 128 128
diff --git a/c0.pgm b/c0.pgm
new file mode 100644
index 00000000..6df908d7
--- /dev/null
+++ b/c0.pgm
@@ -0,0 +1,7 @@
+P2
+4 4
+255
+192 192 192 192
+192 192 192 192
+192 192 192 192
+192 192 192 192
diff --git a/img.mu b/img.mu
index a559e1bb..10f467fa 100644
--- a/img.mu
+++ b/img.mu
@@ -27,9 +27,18 @@ fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk)
   var img-storage: image
   var img/esi: (addr image) <- address img-storage
   load-image img, data-disk
-  render-image screen, img, 0x20/x 0x80/y, 0x100/width, 0x100/height
-  render-image screen, img, 0x120/x 0x180/y, 0x12c/width=300, 0xc8/height=200
-  render-image screen, img, 0x320/x 0x280/y, 0x60/width=96, 0x1c/height=28
+#?   render-image screen, img, 0x20/x, 0x80/y, 0x100/width, 0x100/height
+#?   render-image screen, img, 0x120/x, 0x180/y, 0x12c/width=300, 0xc8/height=200
+#?   render-image screen, img, 0x320/x, 0x280/y, 0x60/width=96, 0x1c/height=28
+
+#?   render-pgm-image screen, img, 0x1c0/x, 0x100/y, 0x12c/width=300, 0xc8/height=200
+#?   draw-box-on-real-screen 0x1bf/x, 0x102/y, 0x1c4/x, 0x104/y, 4/fg
+#?   render-image screen, img, 0x80/x, 0x100/y, 0x12c/width=300, 0xc8/height=200
+
+  set-cursor-position 0/screen, 0/x 2/y
+  render-pgm-image screen, img, 0x200/x, 0x100/y, 0xc0/width, 0xc0/height
+  render-image screen, img, 0x100/x, 0x100/y, 0xc0/width, 0xc0/height
+
 }
 
 fn load-image self: (addr image), data-disk: (addr disk) {
@@ -97,7 +106,10 @@ fn render-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int,
   {
     compare *type-a, 2/pgm
     break-if-!=
-    render-pgm-image screen, img, xmin, ymin, width, height
+    var img2-storage: image
+    var img2/edi: (addr image) <- address img2-storage
+    dither-pgm-with-monochrome img, img2
+    render-pbm-image screen, img2, xmin, ymin, width, height
     return
   }
   {
@@ -177,7 +189,6 @@ fn render-pbm-image screen: (addr screen), _img: (addr image), xmin: int, ymin:
   var zero/eax: int <- copy 0
   var zero-f/xmm0: float <- convert zero
   var y/xmm6: float <- copy zero-f
-  set-cursor-position 0/screen, 0x20/x 0x20/y
   {
     compare y, height-f
     break-if-float>=
@@ -192,12 +203,7 @@ fn render-pbm-image screen: (addr screen), _img: (addr image), xmin: int, ymin:
       imgx-f <- divide xratio
       var imgx/ecx: int <- truncate imgx-f
       var idx/eax: int <- copy imgy
-      {
-        compare idx, 0
-        break-if-<=
-        idx <- decrement
-        idx <- multiply img-width
-      }
+      idx <- multiply img-width
       idx <- add imgx
       # error info in case we rounded wrong and 'index' will fail bounds-check
       compare idx, len
@@ -305,7 +311,6 @@ fn render-pgm-image screen: (addr screen), _img: (addr image), xmin: int, ymin:
   var zero/eax: int <- copy 0
   var zero-f/xmm0: float <- convert zero
   var y/xmm6: float <- copy zero-f
-  set-cursor-position 0/screen, 0x20/x 0x20/y
   {
     compare y, height-f
     break-if-float>=
@@ -320,12 +325,7 @@ fn render-pgm-image screen: (addr screen), _img: (addr image), xmin: int, ymin:
       imgx-f <- divide xratio
       var imgx/ecx: int <- truncate imgx-f
       var idx/eax: int <- copy imgy
-      {
-        compare idx, 0
-        break-if-<=
-        idx <- decrement
-        idx <- multiply img-width
-      }
+      idx <- multiply img-width
       idx <- add imgx
       # error info in case we rounded wrong and 'index' will fail bounds-check
       compare idx, len
@@ -359,6 +359,252 @@ fn nearest-grey level-255: byte -> _/eax: int {
   return result
 }
 
+fn dither-pgm-with-monochrome _src: (addr image), _dest: (addr image) {
+  var src/esi: (addr image) <- copy _src
+  var dest/edi: (addr image) <- copy _dest
+  # copy 'width'
+  var src-width-a/ecx: (addr int) <- get src, width
+  var src-width/ecx: int <- copy *src-width-a
+  {
+    var dest-width-a/edx: (addr int) <- get dest, width
+    copy-to *dest-width-a, src-width
+  }
+  # copy 'height'
+  var src-height-a/edx: (addr int) <- get src, height
+  var src-height/edx: int <- copy *src-height-a
+  {
+    var dest-height-a/ecx: (addr int) <- get dest, height
+    copy-to *dest-height-a, src-height
+  }
+  # transform 'data'
+  var capacity/ebx: int <- copy src-width
+  capacity <- multiply src-height
+  var dest/edi: (addr image) <- copy _dest
+  var dest-data-ah/eax: (addr handle array byte) <- get dest, data
+  populate dest-data-ah, capacity
+  var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah
+  var dest-data/edi: (addr array byte) <- copy _dest-data
+  # needs a buffer to temporarily hold more than 256 levels of precision
+  var buffer-storage: (array int 0xc0000)
+  var buffer/ebx: (addr array int) <- address buffer-storage
+  var src-data-ah/eax: (addr handle array byte) <- get src, data
+  var _src-data/eax: (addr array byte) <- lookup *src-data-ah
+  var src-data/esi: (addr array byte) <- copy _src-data
+  _unordered-monochrome-dither src-data, src-width, src-height, buffer, dest-data
+}
+
+fn _unordered-monochrome-dither src: (addr array byte), width: int, height: int, buf: (addr array int), dest: (addr array byte) {
+  var y/edx: int <- copy 0
+  {
+    compare y, height
+    break-if->=
+#?     psd "y", y, 9/fg, 0/x, y
+    var x/ecx: int <- copy 0
+    {
+      compare x, width
+      break-if->=
+#?       psd "x", x, 3/fg, x, y
+      var error/ebx: int <- _read-buffer buf, x, y, width
+      $_unordered-monochrome-dither:update-error: {
+        var curr/eax: byte <- _read-byte-buffer src, x, y, width
+        error <- add curr
+#?         psd "e", error, 5/fg, x, y
+#?         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, error, 7/fg 0/bg
+        compare error, 0x80
+        {
+          break-if->=
+#?           psd "p", 0, 0x14/fg, x, y
+          _write-byte-buffer dest, x, y, width, 0/black
+          break $_unordered-monochrome-dither:update-error
+        }
+#?         psd "p", 1, 0xf/fg, x, y
+        _write-byte-buffer dest, x, y, width, 1/white
+        error <- subtract 0xff
+      }
+      _diffuse-monochrome-dithering-errors buf, x, y, width, height, error
+#?       {
+#?         compare y, 3
+#?         break-if-!=
+#?         compare x, 4
+#?         break-if->=
+#?         {
+#?           var b/eax: byte <- read-key 0/keyboard
+#?           compare b, 0
+#?           loop-if-=
+#?         }
+#?       }
+      x <- increment
+      loop
+    }
+    move-cursor-to-left-margin-of-next-line 0/screen
+    y <- increment
+    loop
+  }
+}
+
+fn show-errors buf: (addr array int), width: int, height: int {
+  var y/edx: int <- copy 0
+  {
+    compare y, height
+    break-if->=
+    var x/ecx: int <- copy 0
+    {
+      compare x, width
+      break-if->=
+      var error/ebx: int <- _read-buffer buf, x, y, width
+      psd "e", error, 5/fg, x, y
+      x <- increment
+      loop
+    }
+    move-cursor-to-left-margin-of-next-line 0/screen
+    y <- increment
+    loop
+  }
+}
+
+fn psd s: (addr array byte), d: int, fg: int, x: int, y: int {
+#?   {
+#?     compare y, 3
+#?     break-if-=
+#?     return
+#?   }
+#?   {
+#?     compare x, 4
+#?     break-if-<
+#?     return
+#?   }
+  draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg
+  draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg
+}
+
+# Use Floyd-Steinberg algorithm for turning an image of greyscale pixels into
+# one of pure black or white pixels.
+#
+# https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html
+fn _diffuse-monochrome-dithering-errors buf: (addr array int), x: int, y: int, width: int, height: int, error: int {
+  {
+    compare error, 0
+    break-if-!=
+    return
+  }
+  decrement width
+  decrement height
+  # delta = error/16
+#?   show-errors buf, width, height
+#?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, error, 3/fg 0/bg
+#?   move-cursor-to-left-margin-of-next-line 0/screen
+  var delta/ecx: int <- copy error
+  delta <- shift-right-signed 4
+#?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, delta, 2/fg 0/bg
+#?   move-cursor-to-left-margin-of-next-line 0/screen
+  # In Floyd-Steinberg, each pixel X transmits its errors to surrounding
+  # pixels in the following proportion:
+  #           X     7/16
+  #     3/16  5/16  1/16
+  var x/esi: int <- copy x
+  {
+    compare x, width
+    break-if->=
+    var tmp/eax: int <- copy delta
+    var seven/edx: int <- copy 7
+    tmp <- multiply seven
+    var xright/edx: int <- copy x
+    xright <- increment
+    _accumulate-error buf, xright, y, width, tmp
+  }
+#?   show-errors buf, width, height
+  var y/edi: int <- copy y
+  {
+    compare y, height
+    break-if-<
+    return
+  }
+  var ybelow/edi: int <- copy y
+  ybelow <- increment
+  {
+    compare x, 0
+    break-if-<=
+    var tmp/eax: int <- copy delta
+    var three/edx: int <- copy 3
+    tmp <- multiply three
+    var xleft/ebx: int <- copy x
+    xleft <- decrement
+    _accumulate-error buf, xleft, ybelow, width, tmp
+  }
+#?   show-errors buf, width, height
+  {
+    var tmp/eax: int <- copy delta
+    var five/edx: int <- copy 5
+    tmp <- multiply five
+    _accumulate-error buf, x, ybelow, width, tmp
+  }
+#?   show-errors buf, width, height
+  {
+    compare x, width
+    break-if->=
+    var xright/edx: int <- copy x
+    xright <- increment
+    _accumulate-error buf, xright, ybelow, width, delta
+  }
+#?   show-errors buf, width, height
+}
+
+fn _accumulate-error buf: (addr array int), x: int, y: int, width: int, error: int {
+  var curr/ebx: int <- _read-buffer buf, x, y, width
+  curr <- add error
+#?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "{", 7/fg 0/bg
+#?   move-cursor-to-left-margin-of-next-line 0/screen
+#?   show-errors buf, width, 3/height
+  _write-buffer buf, x, y, width, curr
+#?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "===", 7/fg 0/bg
+#?   move-cursor-to-left-margin-of-next-line 0/screen
+#?   show-errors buf, width, 3/height
+#?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "}", 7/fg 0/bg
+#?   move-cursor-to-left-margin-of-next-line 0/screen
+}
+
+fn _read-buffer _buf: (addr array int), x: int, y: int, width: int -> _/ebx: int {
+  var buf/esi: (addr array int) <- copy _buf
+  var idx/ecx: int <- copy y
+  idx <- multiply width
+  idx <- add x
+#?   psd "i", idx, 5/fg, x, y
+  var result-a/eax: (addr int) <- index buf, idx
+  return *result-a
+}
+
+fn _write-buffer _buf: (addr array int), x: int, y: int, width: int, val: int {
+  var buf/esi: (addr array int) <- copy _buf
+  var idx/ecx: int <- copy y
+  idx <- multiply width
+  idx <- add x
+#?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 7/fg 0/bg
+#?   move-cursor-to-left-margin-of-next-line 0/screen
+  var src/eax: int <- copy val
+  var dest-a/edi: (addr int) <- index buf, idx
+  copy-to *dest-a, src
+}
+
+fn _read-byte-buffer _buf: (addr array byte), x: int, y: int, width: int -> _/eax: byte {
+  var buf/esi: (addr array byte) <- copy _buf
+  var idx/ecx: int <- copy y
+  idx <- multiply width
+  idx <- add x
+  var result-a/eax: (addr byte) <- index buf, idx
+  var result/eax: byte <- copy-byte *result-a
+  return result
+}
+
+fn _write-byte-buffer _buf: (addr array byte), x: int, y: int, width: int, val: byte {
+  var buf/esi: (addr array byte) <- copy _buf
+  var idx/ecx: int <- copy y
+  idx <- multiply width
+  idx <- add x
+  var src/eax: byte <- copy val
+  var dest-a/edi: (addr byte) <- index buf, idx
+  copy-byte-to *dest-a, src
+}
+
 # import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255)
 fn initialize-image-from-ppm _self: (addr image), in: (addr stream byte) {
   var self/esi: (addr image) <- copy _self
@@ -455,12 +701,7 @@ fn render-ppm-image screen: (addr screen), _img: (addr image), xmin: int, ymin:
       imgx-f <- divide xratio
       var imgx/ecx: int <- truncate imgx-f
       var idx/eax: int <- copy imgy
-      {
-        compare idx, 0
-        break-if-<=
-        idx <- decrement
-        idx <- multiply img-width
-      }
+      idx <- multiply img-width
       idx <- add imgx
       # . multiply by 3 for the r/g/b channels
       {
diff --git a/white.pgm b/white.pgm
new file mode 100644
index 00000000..95415e56
--- /dev/null
+++ b/white.pgm
@@ -0,0 +1,7 @@
+P2
+4 4
+255
+255 255 255 255
+255 255 255 255
+255 255 255 255
+255 255 255 255