https://github.com/akkartik/mu/blob/main/511image.mu
   1 # Loading images from disk, rendering images to screen.
   2 #
   3 # Currently supports ASCII Netpbm formats.
   4 #   https://en.wikipedia.org/wiki/Netpbm#File_formats
   5 
   6 type image {
   7   type: int  # supported types:
   8              #  1: portable bitmap (P1) - pixels 0 or 1
   9              #  2: portable greymap (P2) - pixels 1-byte greyscale values
  10              #  3: portable pixmap (P3) - pixels 3-byte rgb values
  11   max: int
  12   width: int
  13   height: int
  14   data: (handle array byte)
  15 }
  16 
  17 fn initialize-image _self: (addr image), in: (addr stream byte) {
  18   var self/esi: (addr image) <- copy _self
  19   var mode-storage: slice
  20   var mode/ecx: (addr slice) <- address mode-storage
  21   next-word-skipping-comments in, mode
  22   {
  23     var P1?/eax: boolean <- slice-equal? mode, "P1"
  24     compare P1?, 0/false
  25     break-if-=
  26     var type-a/eax: (addr int) <- get self, type
  27     copy-to *type-a, 1/ppm
  28     initialize-image-from-pbm self, in
  29     return
  30   }
  31   {
  32     var P2?/eax: boolean <- slice-equal? mode, "P2"
  33     compare P2?, 0/false
  34     break-if-=
  35     var type-a/eax: (addr int) <- get self, type
  36     copy-to *type-a, 2/pgm
  37     initialize-image-from-pgm self, in
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - continuation2.mu</title>
<meta name="Generator" content="Vim/8.0">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="none">
<meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #aaaaaa; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #aaaaaa; background-color: #080808; }
a { color:#eeeeee; text-decoration: none; }
a:hover { text-decoration: underline; }
* { font-size: 12pt; font-size: 1em; }
.muControl { color: #c0a020; }
.muRecipe { color: #ff8700; }
.LineNr { color: #444444; }
.Delimiter { color: #800080; }
.Constant { color: #00a0a0; }
.Special { color: #c00000; }
.Comment { color: #9090ff; }
.Comment a { color:#0000ee; text-decoration:underline; }
-->
</style>

<script type='text/javascript'>
<!--

/* function to open any folds containing a jumped-to line before jumping to it */
function JumpToLine()
{
  var lineNum;
  lineNum = window.location.hash;
  lineNum = lineNum.substr(1); /* strip off '#' */

  if (lineNum.indexOf('L') == -1) {
    lineNum = 'L'+lineNum;
  }
  lineElem = document.getElementById(lineNum);
  /* Always jump to new location even if the line was hidden inside a fold, or
   * we corrected the raw number to a line ID.
   */
  if (lineElem) {
    lineElem.scrollIntoView(true);
  }
  return true;
}
if ('onhashchange' in window) {
  window.onhashchange = JumpToLine;
}

-->
</script>
</head>
<body onload='JumpToLine();'>
<pre id='vimCodeElement'>
<span id="L1" class="LineNr"> 1 </span><span class="Comment"># Example program showing that a 'paused' continuation can be 'resumed'</span>
<span id="L2" class="LineNr"> 2 </span><span class="Comment"># multiple times from the same point (but with changes to data).</span>
<span id="L3" class="LineNr"> 3 </span><span class="Comment">#</span>
<span id="L4" class="LineNr"> 4 </span><span class="Comment"># To run:</span>
<span id="L5" class="LineNr"> 5 </span><span class="Comment">#   $ git clone <a href="https://github.com/akkartik/mu">https://github.com/akkartik/mu</a></span>
<span id="L6" class="LineNr"> 6 </span><span class="Comment">#   $ cd mu</span>
<span id="L7" class="LineNr"> 7 </span><span class="Comment">#   $ ./mu continuation2.mu</span>
<span id="L8" class="LineNr"> 8 </span><span class="Comment">#</span>
<span id="L9" class="LineNr"> 9 </span><span class="Comment"># Expected output:</span>
<span id="L10" class="LineNr">10 </span><span class="Comment">#   1</span>
<span id="L11" class="LineNr">11 </span><span class="Comment">#   2</span>
<span id="L12" class="LineNr">12 </span><span class="Comment">#   3</span>
<span id="L13" class="LineNr">13 </span>
<span id="L14" class="LineNr">14 </span><span class="muRecipe">def</span> <a href='continuation2.mu.html#L14'>main</a> [
<span id="L15" class="LineNr">15 </span>  <span class="Constant">local-scope</span>
<span id="L16" class="LineNr">16 </span>  l:&amp;:<a href='064list.mu.html#L6'>list</a>:num <span class="Special">&lt;-</span> copy<span class="Constant"> 0</span>
<span id="L17" class="LineNr">17 </span>  l <span class="Special">&lt;-</span> push<span class="Constant"> 3</span>, l
<span id="L18" class="LineNr">18 </span>  l <span class="Special">&lt;-</span> push<span class="Constant"> 2</span>, l
<span id="L19" class="LineNr">19 </span>  l <span class="Special">&lt;-</span> push<span class="Constant"> 1</span>, l
<span id="L20" class="LineNr">20 </span>  k:continuation <span class="Special">&lt;-</span> <span class="muControl">call-with-continuation-mark</span> <span class="Constant">100/mark</span>, <a href='continuation2.mu.html#L29'>create-yielder</a>, l
<span id="L21" class="LineNr">21 </span>  <span class="Delimiter">{</span>
<span id="L22" class="LineNr">22 </span>    x:num, done?:bool <span class="Special">&lt;-</span> call k
<span id="L23" class="LineNr">23 </span>    <span class="muControl">break-if</span> done?
<span id="L24" class="LineNr">24 </span>    $print x <span class="Constant">10/newline</span>
<span id="L25" class="LineNr">25 </span>   <span class="muControl"> loop</span>
<span id="L26" class="LineNr">26 </span>  <span class="Delimiter">}</span>
<span id="L27" class="LineNr">27 </span>]
<span id="L28" class="LineNr">28 </span>
<span id="L29" class="LineNr">29 </span><span class="muRecipe">def</span> <a href='continuation2.mu.html#L29'>create-yielder</a> l:&amp;:<a href='064list.mu.html#L6'>list</a>:num<span class="muRecipe"> -&gt; </span>n:num, done?:bool [
<span id="L30" class="LineNr">30 </span>  <span class="Constant">local-scope</span>
<span id="L31" class="LineNr">31 </span>  <span class="Constant">load-inputs</span>
<span id="L32" class="LineNr">32 </span>  <span class="muControl">return-continuation-until-mark</span> <span class="Constant">100/mark</span>
<span id="L33" class="LineNr">33 </span>  done? <span class="Special">&lt;-</span> equal l, <span class="Constant">0/nil</span>
<span id="L34" class="LineNr">34 </span>  <span class="muControl">return-if</span> done?, <span class="Constant">0/false</span>
<span id="L35" class="LineNr">35 </span>  n <span class="Special">&lt;-</span> first l
<span id="L36" class="LineNr">36 </span>  l <span class="Special">&lt;-</span> <a href='064list.mu.html#L24'>rest</a> l
<span id="L37" class="LineNr">37 </span>]
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->
class="LineNr"> 373 { 374 break-if->= 375 _write-raw-buffer dest-data, x, y, src-width, 0/black 376 break $dither-pgm-unordered-monochrome:update-error 377 } 378 _write-raw-buffer dest-data, x, y, src-width, 1/white 379 error <- subtract 0xff0000 380 } 381 _diffuse-dithering-error-floyd-steinberg errors, x, y, src-width, src-height, error 382 x <- increment 383 loop 384 } 385 move-cursor-to-left-margin-of-next-line 0/screen 386 y <- increment 387 loop 388 } 389 } 390 391 fn dither-pgm-unordered _src: (addr image), _dest: (addr image) { 392 var src/esi: (addr image) <- copy _src 393 var dest/edi: (addr image) <- copy _dest 394 # copy 'width' 395 var src-width-a/eax: (addr int) <- get src, width 396 var tmp/eax: int <- copy *src-width-a 397 var src-width: int 398 copy-to src-width, tmp 399 { 400 var dest-width-a/edx: (addr int) <- get dest, width 401 copy-to *dest-width-a, tmp 402 } 403 # copy 'height' 404 var src-height-a/eax: (addr int) <- get src, height 405 var tmp/eax: int <- copy *src-height-a 406 var src-height: int 407 copy-to src-height, tmp 408 { 409 var dest-height-a/ecx: (addr int) <- get dest, height 410 copy-to *dest-height-a, tmp 411 } 412 # compute scaling factor 255/max 413 var target-scale/eax: int <- copy 0xff 414 var scale-f/xmm7: float <- convert target-scale 415 var src-max-a/eax: (addr int) <- get src, max 416 var tmp-f/xmm0: float <- convert *src-max-a 417 scale-f <- divide tmp-f 418 # transform 'data' 419 var capacity/ebx: int <- copy src-width 420 capacity <- multiply src-height 421 var dest/edi: (addr image) <- copy _dest 422 var dest-data-ah/eax: (addr handle array byte) <- get dest, data 423 populate dest-data-ah, capacity 424 var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah 425 var dest-data/edi: (addr array byte) <- copy _dest-data 426 # needs a buffer to temporarily hold more than 256 levels of precision 427 var errors-storage: (array int 0xc0000) 428 var errors/ebx: (addr array int) <- address errors-storage 429 var src-data-ah/eax: (addr handle array byte) <- get src, data 430 var _src-data/eax: (addr array byte) <- lookup *src-data-ah 431 var src-data/esi: (addr array byte) <- copy _src-data 432 var y/edx: int <- copy 0 433 { 434 compare y, src-height 435 break-if->= 436 var x/ecx: int <- copy 0 437 { 438 compare x, src-width 439 break-if->= 440 var initial-color/eax: byte <- _read-pgm-buffer src-data, x, y, src-width 441 # . scale to 255 levels 442 var initial-color-int/eax: int <- copy initial-color 443 var initial-color-f/xmm0: float <- convert initial-color-int 444 initial-color-f <- multiply scale-f 445 initial-color-int <- convert initial-color-f 446 var error/esi: int <- _read-dithering-error errors, x, y, src-width 447 # error += (initial-color << 16) 448 { 449 var tmp/eax: int <- copy initial-color-int 450 tmp <- shift-left 0x10 # we have 32 bits; we'll use 16 bits for the fraction and leave 8 for unanticipated overflow 451 error <- add tmp 452 } 453 # nearest-color = nearest(error >> 16) 454 var nearest-color/eax: int <- copy error 455 nearest-color <- shift-right-signed 0x10 456 { 457 compare nearest-color, 0 458 break-if->= 459 nearest-color <- copy 0 460 } 461 { 462 compare nearest-color, 0xf0 463 break-if-<= 464 nearest-color <- copy 0xf0 465 } 466 # . truncate last 4 bits 467 nearest-color <- and 0xf0 468 # error -= (nearest-color << 16) 469 { 470 var tmp/eax: int <- copy nearest-color 471 tmp <- shift-left 0x10 472 error <- subtract tmp 473 } 474 # color-index = (nearest-color >> 4 + 16) 475 var color-index/eax: int <- copy nearest-color 476 color-index <- shift-right 4 477 color-index <- add 0x10 478 var color-index-byte/eax: byte <- copy-byte color-index 479 _write-raw-buffer dest-data, x, y, src-width, color-index-byte 480 _diffuse-dithering-error-floyd-steinberg errors, x, y, src-width, src-height, error 481 x <- increment 482 loop 483 } 484 y <- increment 485 loop 486 } 487 } 488 489 # Use Floyd-Steinberg algorithm for diffusing error at x, y in a 2D grid of 490 # dimensions (width, height) 491 # 492 # https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html 493 # 494 # Error is currently a fixed-point number with 16-bit fraction. But 495 # interestingly this function doesn't care about that. 496 fn _diffuse-dithering-error-floyd-steinberg errors: (addr array int), x: int, y: int, width: int, height: int, error: int { 497 { 498 compare error, 0 499 break-if-!= 500 return 501 } 502 var width-1/esi: int <- copy width 503 width-1 <- decrement 504 var height-1/edi: int <- copy height 505 height-1 <- decrement 506 # delta = error/16 507 #? show-errors errors, width, height, x, y 508 var delta/ecx: int <- copy error 509 delta <- shift-right-signed 4 510 # In Floyd-Steinberg, each pixel X transmits its errors to surrounding 511 # pixels in the following proportion: 512 # X 7/16 513 # 3/16 5/16 1/16 514 var x/edx: int <- copy x 515 { 516 compare x, width-1 517 break-if->= 518 var tmp/eax: int <- copy 7 519 tmp <- multiply delta 520 var xright/edx: int <- copy x 521 xright <- increment 522 _accumulate-dithering-error errors, xright, y, width, tmp 523 } 524 var y/ebx: int <- copy y 525 { 526 compare y, height-1 527 break-if-< 528 return 529 } 530 var ybelow: int 531 copy-to ybelow, y 532 increment ybelow 533 { 534 compare x, 0 535 break-if-<= 536 var tmp/eax: int <- copy 3 537 tmp <- multiply delta 538 var xleft/edx: int <- copy x 539 xleft <- decrement 540 _accumulate-dithering-error errors, xleft, ybelow, width, tmp 541 } 542 { 543 var tmp/eax: int <- copy 5 544 tmp <- multiply delta 545 _accumulate-dithering-error errors, x, ybelow, width, tmp 546 } 547 { 548 compare x, width-1 549 break-if->= 550 var xright/edx: int <- copy x 551 xright <- increment 552 _accumulate-dithering-error errors, xright, ybelow, width, delta 553 } 554 #? show-errors errors, width, height, x, y 555 } 556 557 fn _accumulate-dithering-error errors: (addr array int), x: int, y: int, width: int, error: int { 558 var curr/esi: int <- _read-dithering-error errors, x, y, width 559 curr <- add error 560 _write-dithering-error errors, x, y, width, curr 561 } 562 563 fn _read-dithering-error _errors: (addr array int), x: int, y: int, width: int -> _/esi: int { 564 var errors/esi: (addr array int) <- copy _errors 565 var idx/ecx: int <- copy y 566 idx <- multiply width 567 idx <- add x 568 var result-a/eax: (addr int) <- index errors, idx 569 return *result-a 570 } 571 572 fn _write-dithering-error _errors: (addr array int), x: int, y: int, width: int, val: int { 573 var errors/esi: (addr array int) <- copy _errors 574 var idx/ecx: int <- copy y 575 idx <- multiply width 576 idx <- add x 577 #? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 7/fg 0/bg 578 #? move-cursor-to-left-margin-of-next-line 0/screen 579 var src/eax: int <- copy val 580 var dest-a/edi: (addr int) <- index errors, idx 581 copy-to *dest-a, src 582 } 583 584 fn _read-pgm-buffer _buf: (addr array byte), x: int, y: int, width: int -> _/eax: byte { 585 var buf/esi: (addr array byte) <- copy _buf 586 var idx/ecx: int <- copy y 587 idx <- multiply width 588 idx <- add x 589 var result-a/eax: (addr byte) <- index buf, idx 590 var result/eax: byte <- copy-byte *result-a 591 return result 592 } 593 594 fn _write-raw-buffer _buf: (addr array byte), x: int, y: int, width: int, val: byte { 595 var buf/esi: (addr array byte) <- copy _buf 596 var idx/ecx: int <- copy y 597 idx <- multiply width 598 idx <- add x 599 var src/eax: byte <- copy val 600 var dest-a/edi: (addr byte) <- index buf, idx 601 copy-byte-to *dest-a, src 602 } 603 604 # some debugging helpers 605 fn show-errors errors: (addr array int), width: int, height: int, x: int, y: int { 606 compare y, 1 607 { 608 break-if-= 609 return 610 } 611 compare x, 0 612 { 613 break-if-= 614 return 615 } 616 var y/edx: int <- copy 0 617 { 618 compare y, height 619 break-if->= 620 var x/ecx: int <- copy 0 621 { 622 compare x, width 623 break-if->= 624 var error/esi: int <- _read-dithering-error errors, x, y, width 625 psd "e", error, 5/fg, x, y 626 x <- increment 627 loop 628 } 629 move-cursor-to-left-margin-of-next-line 0/screen 630 y <- increment 631 loop 632 } 633 } 634 635 fn psd s: (addr array byte), d: int, fg: int, x: int, y: int { 636 { 637 compare y, 0x18 638 break-if->= 639 return 640 } 641 { 642 compare y, 0x1c 643 break-if-<= 644 return 645 } 646 { 647 compare x, 0x40 648 break-if->= 649 return 650 } 651 #? { 652 #? compare x, 0x48 653 #? break-if-<= 654 #? return 655 #? } 656 draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg 657 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg 658 } 659 660 fn psx s: (addr array byte), d: int, fg: int, x: int, y: int { 661 #? { 662 #? compare y, 0x60 663 #? break-if->= 664 #? return 665 #? } 666 #? { 667 #? compare y, 0x6c 668 #? break-if-<= 669 #? return 670 #? } 671 { 672 compare x, 0x20 673 break-if->= 674 return 675 } 676 #? { 677 #? compare x, 0x6c 678 #? break-if-<= 679 #? return 680 #? } 681 draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg 682 draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg 683 } 684 685 # import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255) 686 fn initialize-image-from-ppm _self: (addr image), in: (addr stream byte) { 687 var self/esi: (addr image) <- copy _self 688 var curr-word-storage: slice 689 var curr-word/ecx: (addr slice) <- address curr-word-storage 690 # load width, height 691 next-word-skipping-comments in, curr-word 692 var tmp/eax: int <- parse-decimal-int-from-slice curr-word 693 var width/edx: int <- copy tmp 694 next-word-skipping-comments in, curr-word 695 tmp <- parse-decimal-int-from-slice curr-word 696 var height/ebx: int <- copy tmp 697 next-word-skipping-comments in, curr-word 698 # check color levels 699 { 700 tmp <- parse-decimal-int-from-slice curr-word 701 compare tmp, 0xff 702 break-if-= 703 abort "initialize-image-from-ppm: supports exactly 255 levels per rgb channel" 704 } 705 var dest/edi: (addr int) <- get self, max 706 copy-to *dest, tmp 707 # save width, height 708 dest <- get self, width 709 copy-to *dest, width 710 dest <- get self, height 711 copy-to *dest, height 712 # initialize data 713 var capacity/edx: int <- copy width 714 capacity <- multiply height 715 # . multiply by 3 for the r/g/b channels 716 var tmp/eax: int <- copy capacity 717 tmp <- shift-left 1 718 capacity <- add tmp 719 # 720 var data-ah/edi: (addr handle array byte) <- get self, data 721 populate data-ah, capacity 722 var _data/eax: (addr array byte) <- lookup *data-ah 723 var data/edi: (addr array byte) <- copy _data 724 var i/ebx: int <- copy 0 725 { 726 compare i, capacity 727 break-if->= 728 next-word-skipping-comments in, curr-word 729 var src/eax: int <- parse-decimal-int-from-slice curr-word 730 { 731 var dest/ecx: (addr byte) <- index data, i 732 copy-byte-to *dest, src 733 } 734 i <- increment 735 loop 736 } 737 } 738 739 # import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255) 740 fn render-ppm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int { 741 var img/esi: (addr image) <- copy _img 742 # yratio = height/img->height 743 var img-height-a/eax: (addr int) <- get img, height 744 var img-height/xmm0: float <- convert *img-height-a 745 var yratio/xmm1: float <- convert height 746 yratio <- divide img-height 747 # xratio = width/img->width 748 var img-width-a/eax: (addr int) <- get img, width 749 var img-width/ebx: int <- copy *img-width-a 750 var img-width-f/xmm0: float <- convert img-width 751 var xratio/xmm2: float <- convert width 752 xratio <- divide img-width-f 753 # esi = img->data 754 var img-data-ah/eax: (addr handle array byte) <- get img, data 755 var _img-data/eax: (addr array byte) <- lookup *img-data-ah 756 var img-data/esi: (addr array byte) <- copy _img-data 757 var len/edi: int <- length img-data 758 # 759 var one/eax: int <- copy 1 760 var one-f/xmm3: float <- convert one 761 var width-f/xmm4: float <- convert width 762 var height-f/xmm5: float <- convert height 763 var zero/eax: int <- copy 0 764 var zero-f/xmm0: float <- convert zero 765 var y/xmm6: float <- copy zero-f 766 { 767 compare y, height-f 768 break-if-float>= 769 var imgy-f/xmm5: float <- copy y 770 imgy-f <- divide yratio 771 var imgy/edx: int <- truncate imgy-f 772 var x/xmm7: float <- copy zero-f 773 { 774 compare x, width-f 775 break-if-float>= 776 var imgx-f/xmm5: float <- copy x 777 imgx-f <- divide xratio 778 var imgx/ecx: int <- truncate imgx-f 779 var idx/eax: int <- copy imgy 780 idx <- multiply img-width 781 idx <- add imgx 782 # . multiply by 3 for the r/g/b channels 783 { 784 var tmp/ecx: int <- copy idx 785 tmp <- shift-left 1 786 idx <- add tmp 787 } 788 # error info in case we rounded wrong and 'index' will fail bounds-check 789 compare idx, len 790 { 791 break-if-< 792 set-cursor-position 0/screen, 0x20/x 0x20/y 793 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg 794 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg 795 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg 796 } 797 # r channel 798 var r: int 799 { 800 var src-a/eax: (addr byte) <- index img-data, idx 801 var src/eax: byte <- copy-byte *src-a 802 copy-to r, src 803 } 804 idx <- increment 805 # g channel 806 var g: int 807 { 808 var src-a/eax: (addr byte) <- index img-data, idx 809 var src/eax: byte <- copy-byte *src-a 810 copy-to g, src 811 } 812 idx <- increment 813 # b channel 814 var b: int 815 { 816 var src-a/eax: (addr byte) <- index img-data, idx 817 var src/eax: byte <- copy-byte *src-a 818 copy-to b, src 819 } 820 idx <- increment 821 # plot nearest color 822 var color/eax: int <- nearest-color-euclidean r, g, b 823 var screenx/ecx: int <- convert x 824 screenx <- add xmin 825 var screeny/edx: int <- convert y 826 screeny <- add ymin 827 pixel screen, screenx, screeny, color 828 x <- add one-f 829 loop 830 } 831 y <- add one-f 832 loop 833 } 834 } 835 836 fn dither-ppm-unordered _src: (addr image), _dest: (addr image) { 837 var src/esi: (addr image) <- copy _src 838 var dest/edi: (addr image) <- copy _dest 839 # copy 'width' 840 var src-width-a/eax: (addr int) <- get src, width 841 var tmp/eax: int <- copy *src-width-a 842 var src-width: int 843 copy-to src-width, tmp 844 { 845 var dest-width-a/edx: (addr int) <- get dest, width 846 copy-to *dest-width-a, tmp 847 } 848 # copy 'height' 849 var src-height-a/eax: (addr int) <- get src, height 850 var tmp/eax: int <- copy *src-height-a 851 var src-height: int 852 copy-to src-height, tmp 853 { 854 var dest-height-a/ecx: (addr int) <- get dest, height 855 copy-to *dest-height-a, tmp 856 } 857 # compute scaling factor 255/max 858 var target-scale/eax: int <- copy 0xff 859 var scale-f/xmm7: float <- convert target-scale 860 var src-max-a/eax: (addr int) <- get src, max 861 var tmp-f/xmm0: float <- convert *src-max-a 862 scale-f <- divide tmp-f 863 # allocate 'data' 864 var capacity/ebx: int <- copy src-width 865 capacity <- multiply src-height 866 var dest/edi: (addr image) <- copy _dest 867 var dest-data-ah/eax: (addr handle array byte) <- get dest, data 868 populate dest-data-ah, capacity 869 var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah 870 var dest-data/edi: (addr array byte) <- copy _dest-data 871 # error buffers per r/g/b channel 872 var red-errors-storage: (array int 0xc0000) 873 var tmp/eax: (addr array int) <- address red-errors-storage 874 var red-errors: (addr array int) 875 copy-to red-errors, tmp 876 var green-errors-storage: (array int 0xc0000) 877 var tmp/eax: (addr array int) <- address green-errors-storage 878 var green-errors: (addr array int) 879 copy-to green-errors, tmp 880 var blue-errors-storage: (array int 0xc0000) 881 var tmp/eax: (addr array int) <- address blue-errors-storage 882 var blue-errors: (addr array int) 883 copy-to blue-errors, tmp 884 # transform 'data' 885 var src-data-ah/eax: (addr handle array byte) <- get src, data 886 var _src-data/eax: (addr array byte) <- lookup *src-data-ah 887 var src-data/esi: (addr array byte) <- copy _src-data 888 var y/edx: int <- copy 0 889 { 890 compare y, src-height 891 break-if->= 892 var x/ecx: int <- copy 0 893 { 894 compare x, src-width 895 break-if->= 896 # - update errors and compute color levels for current pixel in each channel 897 # update red-error with current image pixel 898 var red-error: int 899 { 900 var tmp/esi: int <- _read-dithering-error red-errors, x, y, src-width 901 copy-to red-error, tmp 902 } 903 { 904 var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 0/red, scale-f 905 add-to red-error, tmp 906 } 907 # recompute red channel for current pixel 908 var red-level: int 909 { 910 var tmp/eax: int <- _error-to-ppm-channel red-error 911 copy-to red-level, tmp 912 } 913 # update green-error with current image pixel 914 var green-error: int 915 { 916 var tmp/esi: int <- _read-dithering-error green-errors, x, y, src-width 917 copy-to green-error, tmp 918 } 919 { 920 var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 1/green, scale-f 921 add-to green-error, tmp 922 } 923 # recompute green channel for current pixel 924 var green-level: int 925 { 926 var tmp/eax: int <- _error-to-ppm-channel green-error 927 copy-to green-level, tmp 928 } 929 # update blue-error with current image pixel 930 var blue-error: int 931 { 932 var tmp/esi: int <- _read-dithering-error blue-errors, x, y, src-width 933 copy-to blue-error, tmp 934 } 935 { 936 var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 2/blue, scale-f 937 add-to blue-error, tmp 938 } 939 # recompute blue channel for current pixel 940 var blue-level: int 941 { 942 var tmp/eax: int <- _error-to-ppm-channel blue-error 943 copy-to blue-level, tmp 944 } 945 # - figure out the nearest color 946 var nearest-color-index/eax: int <- nearest-color-euclidean red-level, green-level, blue-level 947 { 948 var nearest-color-index-byte/eax: byte <- copy-byte nearest-color-index 949 _write-raw-buffer dest-data, x, y, src-width, nearest-color-index-byte 950 } 951 # - diffuse errors 952 var red-level: int 953 var green-level: int 954 var blue-level: int 955 { 956 var tmp-red-level/ecx: int <- copy 0 957 var tmp-green-level/edx: int <- copy 0 958 var tmp-blue-level/ebx: int <- copy 0 959 tmp-red-level, tmp-green-level, tmp-blue-level <- color-rgb nearest-color-index 960 copy-to red-level, tmp-red-level 961 copy-to green-level, tmp-green-level 962 copy-to blue-level, tmp-blue-level 963 } 964 # update red-error 965 var red-level-error/eax: int <- copy red-level 966 red-level-error <- shift-left 0x10 967 subtract-from red-error, red-level-error 968 _diffuse-dithering-error-floyd-steinberg red-errors, x, y, src-width, src-height, red-error 969 # update green-error 970 var green-level-error/eax: int <- copy green-level 971 green-level-error <- shift-left 0x10 972 subtract-from green-error, green-level-error 973 _diffuse-dithering-error-floyd-steinberg green-errors, x, y, src-width, src-height, green-error 974 # update blue-error 975 var blue-level-error/eax: int <- copy blue-level 976 blue-level-error <- shift-left 0x10 977 subtract-from blue-error, blue-level-error 978 _diffuse-dithering-error-floyd-steinberg blue-errors, x, y, src-width, src-height, blue-error 979 # 980 x <- increment 981 loop 982 } 983 y <- increment 984 loop 985 } 986 } 987 988 # convert a single channel for a single image pixel to error space 989 fn _ppm-error buf: (addr array byte), x: int, y: int, width: int, channel: int, _scale-f: float -> _/eax: int { 990 # current image pixel 991 var initial-level/eax: byte <- _read-ppm-buffer buf, x, y, width, channel 992 # scale to 255 levels 993 var initial-level-int/eax: int <- copy initial-level 994 var initial-level-f/xmm0: float <- convert initial-level-int 995 var scale-f/xmm1: float <- copy _scale-f 996 initial-level-f <- multiply scale-f 997 initial-level-int <- convert initial-level-f 998 # switch to fixed-point with 16 bits of precision 999 initial-level-int <- shift-left 0x10 1000 return initial-level-int 1001 } 1002 1003 fn _error-to-ppm-channel error: int -> _/eax: int { 1004 # clamp(error >> 16) 1005 var result/esi: int <- copy error 1006 result <- shift-right-signed 0x10 1007 { 1008 compare result, 0 1009 break-if->= 1010 result <- copy 0 1011 } 1012 { 1013 compare result, 0xff 1014 break-if-<= 1015 result <- copy 0xff 1016 } 1017 return result 1018 } 1019 1020 # read from a buffer containing alternating bytes from r/g/b channels 1021 fn _read-ppm-buffer _buf: (addr array byte), x: int, y: int, width: int, channel: int -> _/eax: byte { 1022 var buf/esi: (addr array byte) <- copy _buf 1023 var idx/ecx: int <- copy y 1024 idx <- multiply width 1025 idx <- add x 1026 var byte-idx/edx: int <- copy 3 1027 byte-idx <- multiply idx 1028 byte-idx <- add channel 1029 var result-a/eax: (addr byte) <- index buf, byte-idx 1030 var result/eax: byte <- copy-byte *result-a 1031 return result 1032 } 1033 1034 # each byte in the image data is a color of the current palette 1035 fn render-raw-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int { 1036 var img/esi: (addr image) <- copy _img 1037 # yratio = height/img->height 1038 var img-height-a/eax: (addr int) <- get img, height 1039 var img-height/xmm0: float <- convert *img-height-a 1040 var yratio/xmm1: float <- convert height 1041 yratio <- divide img-height 1042 # xratio = width/img->width 1043 var img-width-a/eax: (addr int) <- get img, width 1044 var img-width/ebx: int <- copy *img-width-a 1045 var img-width-f/xmm0: float <- convert img-width 1046 var xratio/xmm2: float <- convert width 1047 xratio <- divide img-width-f 1048 # esi = img->data 1049 var img-data-ah/eax: (addr handle array byte) <- get img, data 1050 var _img-data/eax: (addr array byte) <- lookup *img-data-ah 1051 var img-data/esi: (addr array byte) <- copy _img-data 1052 var len/edi: int <- length img-data 1053 # 1054 var one/eax: int <- copy 1 1055 var one-f/xmm3: float <- convert one 1056 var width-f/xmm4: float <- convert width 1057 var height-f/xmm5: float <- convert height 1058 var zero/eax: int <- copy 0 1059 var zero-f/xmm0: float <- convert zero 1060 var y/xmm6: float <- copy zero-f 1061 { 1062 compare y, height-f 1063 break-if-float>= 1064 var imgy-f/xmm5: float <- copy y 1065 imgy-f <- divide yratio 1066 var imgy/edx: int <- truncate imgy-f 1067 var x/xmm7: float <- copy zero-f 1068 { 1069 compare x, width-f 1070 break-if-float>= 1071 var imgx-f/xmm5: float <- copy x 1072 imgx-f <- divide xratio 1073 var imgx/ecx: int <- truncate imgx-f 1074 var idx/eax: int <- copy imgy 1075 idx <- multiply img-width 1076 idx <- add imgx 1077 # error info in case we rounded wrong and 'index' will fail bounds-check 1078 compare idx, len 1079 { 1080 break-if-< 1081 set-cursor-position 0/screen, 0x20/x 0x20/y 1082 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg 1083 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg 1084 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg 1085 } 1086 var color-a/eax: (addr byte) <- index img-data, idx 1087 var color/eax: byte <- copy-byte *color-a 1088 var color-int/eax: int <- copy color 1089 var screenx/ecx: int <- convert x 1090 screenx <- add xmin 1091 var screeny/edx: int <- convert y 1092 screeny <- add ymin 1093 pixel screen, screenx, screeny, color-int 1094 x <- add one-f 1095 loop 1096 } 1097 y <- add one-f 1098 loop 1099 } 1100 } 1101 1102 fn scale-image-height _img: (addr image), width: int -> _/ebx: int { 1103 var img/esi: (addr image) <- copy _img 1104 var img-height/eax: (addr int) <- get img, height 1105 var result-f/xmm0: float <- convert *img-height 1106 var img-width/eax: (addr int) <- get img, width 1107 var img-width-f/xmm1: float <- convert *img-width 1108 result-f <- divide img-width-f 1109 var width-f/xmm1: float <- convert width 1110 result-f <- multiply width-f 1111 var result/ebx: int <- convert result-f 1112 return result 1113 } 1114 1115 fn next-word-skipping-comments line: (addr stream byte), out: (addr slice) { 1116 next-word line, out 1117 var retry?/eax: boolean <- slice-starts-with? out, "#" 1118 compare retry?, 0/false 1119 loop-if-!= 1120 }