~/bend-docscommunity

src/jpeg_enc.bend checks

raw source on the hub · import 0x57618559b9b26dd8415f821176a3e692/src/jpeg_enc.bend as Jpeg_enc

src/jpeg_enc: baseline JPEG encode. Sequential 8-bit Huffman (SOF0) and JFIF APP0. Colour is 4:4:4 YCbCr, no subsampling: one 8 by 8 block per component per MCU. Samples are packed 0xAARRGGBB, the layout decode returns; the alpha byte is ignored. Quantisation steps are all 1, so an integer coefficient is kept whole. The forward DCT (T.81 A.3.3) pairs samples that share a cosine weight, then rounds once with the same fixed-point product as a matrix column. Both components share the Annex K luminance Huffman tables. A coefficient indexes its symbol. The scan does not walk the table. A mixed MCU reads each pixel once and keeps the three level planes in arrays. Quant steps are 1, so the mixed scan emits coefficients without dividing. A neutral solid (R = G = B = 128) is an all-zero block and round-trips exactly. Other colours are lossy. Progressive and arithmetic frames are not written. Width and height must sit in 1..65535; anything else is SOI then EOI.

2 imports
import Base
import ./jpeg.bend as Jpeg

Types

type Put source · line 19 · raw

Data

bytes emitted so far (newest first), the open byte, and how many bits it holds

type Kern source · line 23 · raw

Data

cosine weights shared by symmetric samples: DC, two even weights, four odd weights

type Ctx source · line 27 · raw

Data

the cosine kernel, zigzag, and the all-ones quant table

type Book source · line 31 · raw

Type

DC and AC symbols indexed to a packed code: length in the high half, code in the low half

type Code source · line 35 · raw

Data

one looked-up Huffman code

type Oct source · line 39 · raw

Data

eight values, the first at frequency 0

type Planes source · line 43 · raw

Type

the sample plane, the frequency plane, and the next column of one forward DCT

type Tile source · line 47 · raw

Type

one MCU: packed pixels, three level planes, and whether each plane is the neutral level

type Scan source · line 51 · raw

Type

frequency plane, the open zero run, and the AC coder. Steps are 1, so no quant list.

Definitions

def encode.join source · line 54 · raw

@aa:List<&2, U32> -> @bb:List<&2, U32> -> List<&2, U32>

def encode.len source · line 57 · raw

@xs:List<&2, U32> -> U32

def encode.seg source · line 60 · raw

@+mark:U32 -> @+body:List<&2, U32> -> List<&2, U32>

def encode.app0 source · line 64 · raw

List<&2, U32>

def encode.quant source · line 67 · raw

List<&2, U32>

def encode.dqt source · line 70 · raw

List<&2, U32>

def encode.sofbody source · line 73 · raw

@+ww:U32 -> @+hh:U32 -> List<&2, U32>

def encode.sof source · line 76 · raw

@+ww:U32 -> @+hh:U32 -> List<&2, U32>

def encode.dccounts source · line 79 · raw

List<&2, U32>

def encode.dcsyms source · line 82 · raw

List<&2, U32>

def encode.accounts source · line 85 · raw

List<&2, U32>

def encode.acsyms source · line 88 · raw

List<&2, U32>

def encode.dht source · line 97 · raw

@+cls:U32 -> @+counts:List<&2, U32> -> @+syms:List<&2, U32> -> List<&2, U32>

def encode.sos source · line 100 · raw

List<&2, U32>

def encode.header source · line 103 · raw

@+ww:U32 -> @+hh:U32 -> @+dcc:List<&2, U32> -> @+dcs:List<&2, U32> -> @+acc:List<&2, U32> -> @+acs:List<&2, U32> -> List<&2, U32>

def encode.kern source · line 115 · raw

@+cs:List<&2, U32> -> Kern

C[0][0], C[0][2], C[0][6], and the first four weights of frequency 1

def encode.kern.drop source · line 119 · raw

@kern:Kern -> U32

def encode.ctx source · line 131 · raw

Ctx

def encode.word source · line 135 · raw

@+len:U32 -> @+code:U32 -> U32

length sits above the 16-bit code

def encode.bad.b source · line 138 · raw

@bw:Bool -> @bh:Bool -> Bool

def encode.bad.h source · line 145 · raw

@zz:Bool -> @bw:Bool -> @bh:Bool -> Bool

def encode.bad.w source · line 152 · raw

@zz:Bool -> @zh:Bool -> @bw:Bool -> @bh:Bool -> Bool

def encode.bad source · line 159 · raw

@+ww:U32 -> @+hh:U32 -> Bool

def encode.put0 source · line 162 · raw

Put

def encode.stuff source · line 166 · raw

@out:List<&2, U32> -> @+bb:U32 -> Put

a finished byte. Bytes are kept as written; encode.pad stuffs them once, at the end.

def encode.bit.n source · line 169 · raw

@full:Bool -> @out:List<&2, U32> -> @+nb:U32 -> @+nn:U32 -> Put

def encode.bit source · line 176 · raw

@pp:Put -> @+bit:U32 -> Put

def encode.bits.go source · line 181 · raw

@left:Nat -> @pp:Put -> @+code:U32 -> Put

def encode.bits source · line 189 · raw

@pp:Put -> @+len:U32 -> @+code:U32 -> Put

def encode.stuff.put source · line 193 · raw

@ff:Bool -> @+bb:U32 -> @acc:List<&2, U32> -> List<&2, U32>

one byte put in front of the stuffed bytes after it: 255 is followed by a 0

def encode.stuff.all source · line 202 · raw

@out:List<&2, U32> -> @acc:List<&2, U32> -> List<&2, U32>

the written bytes, newest first, stuffed and put in order onto acc: every 255 is followed by a 0 (T.81 F.1.2.3). U32.is_eq, not a literal pattern, so a law reaches a symbolic byte.

def encode.pad.n source · line 209 · raw

@zz:Bool -> @out:List<&2, U32> -> @+buf:U32 -> @+nn:U32 -> List<&2, U32>

def encode.pad source · line 218 · raw

@pp:Put -> List<&2, U32>

the entropy-coded bytes: the open byte padded with 1 bits, then every byte stuffed, in order

def encode.drop source · line 223 · raw

@xs:List<&2, U32> -> U32

def encode.fill source · line 227 · raw

@syms:List<&2, U32> -> @codes:List<&2, U32> -> @lens:List<&2, U32> -> @aa:Array<U32> -> Array<U32>

one canonical table, stored so the symbol is the index

def encode.huff.of source · line 250 · raw

@hh:0x57618559b9b26dd8415f821176a3e692/src/jpeg.Huff -> Array<U32>

def encode.huff source · line 255 · raw

@+counts:List<&2, U32> -> @+syms:List<&2, U32> -> Array<U32>

def encode.book source · line 258 · raw

@+dcc:List<&2, U32> -> @+dcs:List<&2, U32> -> @+acc:List<&2, U32> -> @+acs:List<&2, U32> -> Book

def encode.cat.pick source · line 262 · raw

@lt:Bool -> @+cc:U32 -> @+rest:U32 -> U32

the first of two sizes when the value is below the limit, else the second

def encode.cat.go source · line 273 · raw

@left:Nat -> @+aa:U32 -> @+cc:U32 -> @+lim:U32 -> U32

the size of a magnitude: the first cc with aa below lim = 2^cc, left sizes tried, else cc. Each comparison is made with a constant limit, so a law over a symbolic value keeps one comparison per size.

def encode.cat source · line 282 · raw

@+vv:U32 -> U32

def encode.mag.s source · line 285 · raw

@+ss:U32 -> @+cat:U32 -> @+vv:U32 -> U32

def encode.mag source · line 292 · raw

@+cat:U32 -> @+vv:U32 -> U32

def encode.magp.z source · line 295 · raw

@zz:Bool -> @pp:Put -> @+cat:U32 -> @+vv:U32 -> Put

def encode.coded source · line 303 · raw

@got:Pair(Array<U32>, U32) -> Pair(Array<U32>, Code)

the indexed word is the length and the code

def encode.sym source · line 307 · raw

@aa:Array<U32> -> @+sym:U32 -> Pair(Array<U32>, Code)

def encode.dc.use source · line 310 · raw

@got:Pair(Array<U32>, Code) -> @pp:Put -> @+cat:U32 -> @+diff:U32 -> Pair(Array<U32>, Put)

def encode.dc source · line 315 · raw

@aa:Array<U32> -> @+pp:Put -> @+diff:U32 -> Pair(Array<U32>, Put)

def encode.clip.hi source · line 319 · raw

@hi:Bool -> @+vv:U32 -> @+lim:U32 -> U32

def encode.clip.lo source · line 326 · raw

@lo:Bool -> @+vv:U32 -> @+lim:U32 -> U32

def encode.clip.b source · line 334 · raw

@pos:Bool -> @+vv:U32 -> @+lim:U32 -> U32

by the sign, asked with U32.is_eq so a law reaches a symbolic value

def encode.clip source · line 341 · raw

@+vv:U32 -> @+lim:U32 -> U32

def encode.dcbias.b source · line 347 · raw

@pos:Bool -> @+vv:U32 -> U32

a DC clipped to T.81's range for 8-bit samples, -1024 to 1023, kept as that plus 1024 (0 to 2047), so the difference of two DCs has at most 11 bits (F.1.2.1) and is found without wrapping. The level shift keeps the forward DCT's DC in -1024..1015, so the clip never changes it.

def encode.dcbias source · line 355 · raw

@+vv:U32 -> U32

a DC clipped to -1024 to 1023, plus 1024

def encode.magp.n source · line 359 · raw

@zz:Bool -> @pp:Put -> @+cat:U32 -> @+mm:U32 -> Put

a negative DC difference -mm: its size's code, then its magnitude bits, 2^size - 1 - mm (F.1.2.1)

def encode.dc.use.neg source · line 368 · raw

@got:Pair(Array<U32>, Code) -> @pp:Put -> @+cat:U32 -> @+mm:U32 -> Pair(Array<U32>, Put)

the size's code of a negative DC difference, then its magnitude

def encode.dc.neg source · line 374 · raw

@aa:Array<U32> -> @+pp:Put -> @+mm:U32 -> Pair(Array<U32>, Put)

a negative DC difference, -mm

def encode.dcdiff.b source · line 379 · raw

@up:Bool -> @aa:Array<U32> -> @pp:Put -> @+bb:U32 -> @+pb:U32 -> Pair(Array<U32>, Put)

the DC difference of two biased DCs, by its sign

def encode.dcdiff source · line 387 · raw

@aa:Array<U32> -> @pp:Put -> @+bb:U32 -> @+pb:U32 -> Pair(Array<U32>, Put)

the DC difference of biased DC bb from biased predictor pb

def encode.emit source · line 390 · raw

@got:Pair(Array<U32>, Code) -> @pp:Put -> Pair(Array<U32>, Put)

def encode.eob source · line 395 · raw

@need:Bool -> @aa:Array<U32> -> @pp:Put -> Pair(Array<U32>, Put)

def encode.eob.on source · line 402 · raw

@need:Bool -> @st:Pair(Array<U32>, Put) -> Pair(Array<U32>, Put)

def encode.zrls.one source · line 407 · raw

@st:Pair(Array<U32>, Put) -> Pair(Array<U32>, Put)

def encode.zrls source · line 412 · raw

@left:Nat -> @st:Pair(Array<U32>, Put) -> Pair(Array<U32>, Put)

def encode.ac.mag source · line 419 · raw

@got:Pair(Array<U32>, Put) -> @+cat:U32 -> @+clip:U32 -> Pair(Array<U32>, Put)

def encode.ac.hit source · line 424 · raw

@got:Pair(Array<U32>, Put) -> @+sym:U32 -> @+cat:U32 -> @+clip:U32 -> Pair(Array<U32>, Put)

def encode.ac.step source · line 429 · raw

@+cc:U32 -> @+run:U32 -> @st:Pair(Array<U32>, Put) -> Pair(Array<U32>, Put)

def encode.ac.run source · line 438 · raw

@zero:Bool -> @+run:U32 -> U32

the zero run after one more coefficient

def encode.ac.at source · line 447 · raw

@zero:Bool -> @+cc:U32 -> @+run:U32 -> @st:Pair(Array<U32>, Put) -> Pair(Array<U32>, Put)

one more coefficient: a zero only lengthens the run

def encode.ac source · line 456 · raw

@zz:List<&2, U32> -> @+run:U32 -> @st:Pair(Array<U32>, Put) -> Pair(Array<U32>, Put)

the AC coefficients in zigzag order. U32.is_eq, not a literal pattern, so a law reaches a symbolic coefficient.

def encode.qnz source · line 463 · raw

@+qq:U32 -> U32

def encode.qdiv.s source · line 470 · raw

@+ss:U32 -> @+vv:U32 -> @+qq:U32 -> U32

def encode.qdiv source · line 477 · raw

@+vv:U32 -> @+qq:U32 -> U32

def encode.block.join source · line 480 · raw

@got:Pair(Array<U32>, Put) -> @dc2:Array<U32> -> @+dd:U32 -> Pair(Book, Pair(U32, Put))

def encode.block.dc source · line 484 · raw

@got:Pair(Array<U32>, Put) -> @ac:Array<U32> -> @+dd:U32 -> @rest:List<&2, U32> -> Pair(Book, Pair(U32, Put))

def encode.block.ac source · line 490 · raw

@book:Book -> @pp:Put -> @+pred:U32 -> @+dc0:U32 -> @rest:List<&2, U32> -> Pair(Book, Pair(U32, Put))

the DC, clipped to T.81's 8-bit range and biased by 1024, is written as its difference from the last and kept, so the next block can predict from it

def encode.block.go source · line 496 · raw

@book:Book -> @pp:Put -> @+pred:U32 -> @zz:List<&2, U32> -> Pair(Book, Pair(U32, Put))

def encode.add64.acc source · line 503 · raw

@+ph:U32 -> @+pl:U32 -> @acc:Pair(U32, U32) -> Pair(U32, U32)

def encode.add64 source · line 510 · raw

@prod:Pair(U32, U32) -> @acc:Pair(U32, U32) -> Pair(U32, U32)

add a signed product onto a 64-bit accumulator

def encode.mac source · line 516 · raw

@+coef:U32 -> @+samp:U32 -> @acc:Pair(U32, U32) -> Pair(U32, U32)

one more product on the accumulator

def encode.q17 source · line 520 · raw

@acc:Pair(U32, U32) -> U32

the same rounding the matrix product applies to a finished sum

def encode.scale source · line 525 · raw

@+coef:U32 -> @+samp:U32 -> U32

def encode.two source · line 528 · raw

@+c0:U32 -> @+x0:U32 -> @+c1:U32 -> @+x1:U32 -> U32

def encode.two.n source · line 531 · raw

@+c0:U32 -> @+x0:U32 -> @+c1:U32 -> @+x1:U32 -> U32

def encode.four source · line 534 · raw

@+c0:U32 -> @+s0:U32 -> @+c1:U32 -> @+s1:U32 -> @+c2:U32 -> @+s2:U32 -> @+c3:U32 -> @+s3:U32 -> U32

def encode.even source · line 538 · raw

@+s0:U32 -> @+s1:U32 -> @+s2:U32 -> @+s3:U32 -> @+s4:U32 -> @+s5:U32 -> @+s6:U32 -> @+s7:U32 -> @+k0:U32 -> @+k2:U32 -> @+k6:U32 -> Oct

even frequencies: symmetric samples share one weight

def encode.odd source · line 561 · raw

@+d0:U32 -> @+d1:U32 -> @+d2:U32 -> @+d3:U32 -> @+o0:U32 -> @+o1:U32 -> @+o2:U32 -> @+o3:U32 -> Oct

odd frequencies: antisymmetric samples share one weight

def encode.dct8.od source · line 568 · raw

@+f0:U32 -> @+f2:U32 -> @+f4:U32 -> @+f6:U32 -> @od:Oct -> Oct

def encode.dct8.at source · line 574 · raw

@ev:Oct -> @od:Oct -> Oct

def encode.dct8.k source · line 580 · raw

@+s0:U32 -> @+s1:U32 -> @+s2:U32 -> @+s3:U32 -> @+s4:U32 -> @+s5:U32 -> @+s6:U32 -> @+s7:U32 -> @kern:Kern -> Oct

def encode.dct8 source · line 597 · raw

@samp:Oct -> @kern:Kern -> Oct

one 8-point transform. Pairing is the matrix product, so the rounded value matches.

def encode.take source · line 602 · raw

@row:List<&2, U32> -> Oct

def encode.put8 source · line 610 · raw

@+f0:U32 -> @+f1:U32 -> @+f2:U32 -> @+f3:U32 -> @+f4:U32 -> @+f5:U32 -> @+f6:U32 -> @+f7:U32 -> @aa:Array<U32> -> @+at:U32 -> @+step:U32 -> Array<U32>

def encode.lay source · line 633 · raw

@oo:Oct -> @aa:Array<U32> -> @+at:U32 -> Array<U32>

def encode.scatter source · line 638 · raw

@oo:Oct -> @aa:Array<U32> -> @+at:U32 -> Array<U32>

def encode.oct.pack source · line 643 · raw

@+s0:U32 -> @+s1:U32 -> @+s2:U32 -> @+s3:U32 -> @+s4:U32 -> @+s5:U32 -> @+s6:U32 -> @+s7:U32 -> @aa:Array<U32> -> Pair(Array<U32>, Oct)

def encode.oct.g7 source · line 656 · raw

@got:Pair(Array<U32>, U32) -> @+s0:U32 -> @+s1:U32 -> @+s2:U32 -> @+s3:U32 -> @+s4:U32 -> @+s5:U32 -> @+s6:U32 -> Pair(Array<U32>, Oct)

def encode.oct.g6 source · line 669 · raw

@got:Pair(Array<U32>, U32) -> @+s0:U32 -> @+s1:U32 -> @+s2:U32 -> @+s3:U32 -> @+s4:U32 -> @+s5:U32 -> @+i7:U32 -> Pair(Array<U32>, Oct)

def encode.oct.g5 source · line 682 · raw

@got:Pair(Array<U32>, U32) -> @+s0:U32 -> @+s1:U32 -> @+s2:U32 -> @+s3:U32 -> @+s4:U32 -> @+i6:U32 -> @+i7:U32 -> Pair(Array<U32>, Oct)

def encode.oct.g4 source · line 695 · raw

@got:Pair(Array<U32>, U32) -> @+s0:U32 -> @+s1:U32 -> @+s2:U32 -> @+s3:U32 -> @+i5:U32 -> @+i6:U32 -> @+i7:U32 -> Pair(Array<U32>, Oct)

def encode.oct.g3 source · line 708 · raw

@got:Pair(Array<U32>, U32) -> @+s0:U32 -> @+s1:U32 -> @+s2:U32 -> @+i4:U32 -> @+i5:U32 -> @+i6:U32 -> @+i7:U32 -> Pair(Array<U32>, Oct)

def encode.oct.g2 source · line 721 · raw

@got:Pair(Array<U32>, U32) -> @+s0:U32 -> @+s1:U32 -> @+i3:U32 -> @+i4:U32 -> @+i5:U32 -> @+i6:U32 -> @+i7:U32 -> Pair(Array<U32>, Oct)

def encode.oct.g1 source · line 734 · raw

@got:Pair(Array<U32>, U32) -> @+s0:U32 -> @+i2:U32 -> @+i3:U32 -> @+i4:U32 -> @+i5:U32 -> @+i6:U32 -> @+i7:U32 -> Pair(Array<U32>, Oct)

def encode.oct.g0 source · line 747 · raw

@got:Pair(Array<U32>, U32) -> @+i1:U32 -> @+i2:U32 -> @+i3:U32 -> @+i4:U32 -> @+i5:U32 -> @+i6:U32 -> @+i7:U32 -> Pair(Array<U32>, Oct)

def encode.oct.stride source · line 761 · raw

@aa:Array<U32> -> @+at:U32 -> @+step:U32 -> Pair(Array<U32>, Oct)

eight samples starting at at, every step slots

def encode.col.use source · line 771 · raw

@got:Pair(Array<U32>, Oct) -> @kern:Kern -> Pair(Array<U32>, Oct)

def encode.col source · line 777 · raw

@arr:Array<U32> -> @+at:U32 -> @kern:Kern -> Pair(Array<U32>, Oct)

one frequency column, read straight from the plane

def encode.pass1 source · line 780 · raw

@rows:List<&2, List<&2, U32>> -> @+kern:Kern -> @arr:Array<U32> -> @+at:U32 -> Array<U32>

def encode.pix.sink source · line 788 · raw

@aa:Array<U32> -> U32

def encode.book.sink source · line 795 · raw

@book:Book -> U32

def encode.pass2.done source · line 800 · raw

@st:Planes -> @kern:Kern -> Array<U32>

def encode.pass2.put source · line 808 · raw

@got:Pair(Array<U32>, Oct) -> @dst:Array<U32> -> @+at:U32 -> Planes

def encode.pass2.step source · line 813 · raw

@st:Planes -> @kern:Kern -> Planes

def encode.pass2 source · line 818 · raw

@left:Nat -> @st:Planes -> @+kern:Kern -> Array<U32>

def encode.pass.rows.done source · line 825 · raw

@st:Planes -> @kern:Kern -> Array<U32>

def encode.pass.rows.lay source · line 833 · raw

@got:Pair(Array<U32>, Oct) -> @+kern:Kern -> @dst:Array<U32> -> @+at:U32 -> Planes

def encode.pass.rows.next source · line 838 · raw

@st:Planes -> @+kern:Kern -> Planes

def encode.pass.rows source · line 844 · raw

@left:Nat -> @st:Planes -> @+kern:Kern -> Array<U32>

eight row transforms. left rows are still unread.

def encode.pass.mid source · line 851 · raw

@src:Array<U32> -> @+kern:Kern -> Array<U32>

def encode.freq source · line 855 · raw

@src:Array<U32> -> @+kern:Kern -> Array<U32>

level plane in, natural-order frequencies out

def encode.drain.pack source · line 858 · raw

@got:Pair(Array<U32>, U32) -> @acc:List<&2, U32> -> Pair(Array<U32>, Pair(U32, List<&2, U32>))

def encode.drain.step source · line 863 · raw

@st:Pair(Array<U32>, Pair(U32, List<&2, U32>)) -> @+idx:Nat -> Pair(Array<U32>, Pair(U32, List<&2, U32>))

def encode.drain.done source · line 867 · raw

@st:Pair(Array<U32>, Pair(U32, List<&2, U32>)) -> List<&2, U32>

def encode.drain source · line 873 · raw

@left:Nat -> @st:Pair(Array<U32>, Pair(U32, List<&2, U32>)) -> List<&2, U32>

read index 63 first, so the cons list comes out in order

def encode.drain.st source · line 880 · raw

@got:Pair(Array<U32>, U32) -> Pair(Array<U32>, Pair(U32, List<&2, U32>))

def encode.fdct.go source · line 885 · raw

@src:Array<U32> -> @kern:Kern -> List<&2, U32>

def encode.fdct source · line 888 · raw

@rows:List<&2, List<&2, U32>> -> @+cs:List<&2, U32> -> List<&2, U32>

def encode.level source · line 892 · raw

@xs:List<&2, U32> -> List<&2, U32>

def encode.levels source · line 899 · raw

@rows:List<&2, List<&2, U32>> -> List<&2, List<&2, U32>>

def encode.sat source · line 906 · raw

@over:Bool -> @+xx:U32 -> @+lim:U32 -> U32

def encode.sat8 source · line 913 · raw

@over:Bool -> @+vv:U32 -> U32

def encode.y source · line 920 · raw

@+rr:U32 -> @+gg:U32 -> @+bb:U32 -> U32

def encode.cb source · line 923 · raw

@+rr:U32 -> @+gg:U32 -> @+bb:U32 -> U32

def encode.cr source · line 926 · raw

@+rr:U32 -> @+gg:U32 -> @+bb:U32 -> U32

def encode.ych source · line 929 · raw

@+rr:U32 -> @+gg:U32 -> @+bb:U32 -> U32

def encode.cbch source · line 933 · raw

@+rr:U32 -> @+gg:U32 -> @+bb:U32 -> U32

def encode.crch source · line 937 · raw

@+rr:U32 -> @+gg:U32 -> @+bb:U32 -> U32

def encode.chan.w source · line 941 · raw

@+ch:U32 -> @+rr:U32 -> @+gg:U32 -> @+bb:U32 -> U32

def encode.chan source · line 950 · raw

@+pix:U32 -> @+ch:U32 -> U32

def encode.pix.load source · line 954 · raw

@left:Nat -> @px:List<&2, U32> -> @aa:Array<U32> -> @+ii:U32 -> Array<U32>

the first left samples, then the rest of the list is unused

def encode.sample.use source · line 964 · raw

@got:Pair(Array<U32>, U32) -> @+ch:U32 -> Pair(Array<U32>, U32)

def encode.sample source · line 969 · raw

@aa:Array<U32> -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> @+ch:U32 -> Pair(Array<U32>, U32)

one index into the sample array, instead of a walk from the head of the list

def encode.flat.both source · line 974 · raw

@aa:Bool -> @bb:Bool -> Bool

both samples are the neutral level

def encode.rowpix.step source · line 983 · raw

@+ss:U32 -> @got:Pair(Array<U32>, Pair(List<&2, U32>, Bool)) -> Pair(Array<U32>, Pair(List<&2, U32>, Bool))

def encode.rowpix.one source · line 987 · raw

@+ss:U32 -> @aa:Array<U32> -> Pair(Array<U32>, Pair(List<&2, U32>, Bool))

def encode.rowpix source · line 991 · raw

@left:Nat -> @got:Pair(Array<U32>, U32) -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> @+ch:U32 -> Pair(Array<U32>, Pair(List<&2, U32>, Bool))

got is the sample at x. left counts it and the samples after it.

def encode.rows.join source · line 1012 · raw

@+row:List<&2, U32> -> @rf:Bool -> @rest:Pair(Array<U32>, Pair(List<&2, List<&2, U32>>, Bool)) -> Pair(Array<U32>, Pair(List<&2, List<&2, U32>>, Bool))

def encode.rows.one source · line 1020 · raw

@+row:List<&2, U32> -> @rf:Bool -> @aa:Array<U32> -> Pair(Array<U32>, Pair(List<&2, List<&2, U32>>, Bool))

def encode.rows source · line 1024 · raw

@left:Nat -> @got:Pair(Array<U32>, Pair(List<&2, U32>, Bool)) -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> @+ch:U32 -> Pair(Array<U32>, Pair(List<&2, List<&2, U32>>, Bool))

got is the row at y. left counts it and the rows after it.

def encode.droprows source · line 1045 · raw

@rows:List<&2, List<&2, U32>> -> U32

def encode.zzq.take source · line 1054 · raw

@qs:List<&2, U32> -> Pair(U32, List<&2, U32>)

the next quant step, or 0 when the table has run out (qdiv turns 0 into 1)

def encode.zzq.go source · line 1062 · raw

@rest:List<&2, U32> -> @got:Pair(Array<U32>, U32) -> @qv:Pair(U32, List<&2, U32>) -> @acc:List<&2, U32> -> List<&2, U32>

got is the coefficient at the zigzag index just read. rest is the indexes after it.

def encode.zzq.enter source · line 1084 · raw

@zig:List<&2, U32> -> @arr:Array<U32> -> @qs:List<&2, U32> -> @acc:List<&2, U32> -> List<&2, U32>

def encode.zzq.load source · line 1093 · raw

@nat:List<&2, U32> -> @arr:Array<U32> -> @+at:U32 -> Array<U32>

def encode.zzq source · line 1101 · raw

@zig:List<&2, U32> -> @+nat:List<&2, U32> -> @+qs:List<&2, U32> -> @acc:List<&2, U32> -> List<&2, U32>

the coefficient block is an array, so each zigzag step is an index

def encode.zz source · line 1105 · raw

@flat:Bool -> @rows:List<&2, List<&2, U32>> -> @+kern:Kern -> @+zig:List<&2, U32> -> @+qs:List<&2, U32> -> List<&2, U32>

128 level-shifts to 0, and the integer DCT of that block is 0, so quant by 1 stays 0

def encode.block.pair source · line 1122 · raw

@got:Pair(Book, Pair(U32, Put)) -> @aa:Array<U32> -> Pair(Array<U32>, Pair(Book, Pair(U32, Put)))

def encode.block.use source · line 1127 · raw

@book:Book -> @pp:Put -> @+pred:U32 -> @got:Pair(Array<U32>, Pair(List<&2, List<&2, U32>>, Bool)) -> @+kern:Kern -> @+zig:List<&2, U32> -> @+qs:List<&2, U32> -> Pair(Array<U32>, Pair(Book, Pair(U32, Put)))

def encode.block source · line 1139 · raw

@book:Book -> @pp:Put -> @+pred:U32 -> @aa:Array<U32> -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> @+ch:U32 -> @ctx:Ctx -> Pair(Array<U32>, Pair(Book, Pair(U32, Put)))

def encode.nx source · line 1158 · raw

@more:Bool -> @+mx:U32 -> U32

def encode.ny source · line 1165 · raw

@more:Bool -> @+my:U32 -> U32

def encode.step.end source · line 1172 · raw

@got:Pair(Array<U32>, Pair(Book, Pair(U32, Put))) -> @+ydc:U32 -> @+bdc:U32 -> Pair(Array<U32>, Pair(Book, Pair(U32, Pair(U32, Pair(U32, Put)))))

def encode.scan.hit source · line 1180 · raw

@got:Pair(Array<U32>, Put) -> @arr:Array<U32> -> Scan

def encode.scan.skip source · line 1184 · raw

@sc:Scan -> @+cc:U32 -> Scan

def encode.scan.keep source · line 1190 · raw

@sc:Scan -> @+cc:U32 -> Scan

def encode.scan.bump source · line 1195 · raw

@zz:Bool -> @sc:Scan -> @+cc:U32 -> Scan

def encode.scan.val source · line 1203 · raw

@got:Pair(Array<U32>, U32) -> @+run:U32 -> @st:Pair(Array<U32>, Put) -> Scan

step is 1, so the frequency is the coefficient

def encode.scan.step source · line 1208 · raw

@sc:Scan -> @+gg:U32 -> Scan

def encode.scan.end source · line 1213 · raw

@sc:Scan -> Pair(Array<U32>, Put)

def encode.scan.ac source · line 1220 · raw

@rest:List<&2, U32> -> @sc:Scan -> Pair(Array<U32>, Put)

the rest of the zigzag, indexed in the frequency plane

def encode.scan.book source · line 1227 · raw

@got:Pair(Array<U32>, Put) -> @dc:Array<U32> -> @+dd:U32 -> Pair(Book, Pair(U32, Put))

def encode.scan.join source · line 1231 · raw

@got:Pair(Array<U32>, Put) -> @ac:Array<U32> -> @+dd:U32 -> @rest:List<&2, U32> -> @arr:Array<U32> -> Pair(Book, Pair(U32, Put))

def encode.scan.dc source · line 1241 · raw

@book:Book -> @pp:Put -> @+pred:U32 -> @+d0:U32 -> @rest:List<&2, U32> -> @arr:Array<U32> -> Pair(Book, Pair(U32, Put))

def encode.scan.open source · line 1254 · raw

@rest:List<&2, U32> -> @got:Pair(Array<U32>, U32) -> @book:Book -> @pp:Put -> @+pred:U32 -> Pair(Book, Pair(U32, Put))

def encode.scan.none source · line 1264 · raw

@book:Book -> @pp:Put -> @arr:Array<U32> -> @+pred:U32 -> Pair(Book, Pair(U32, Put))

def encode.scan source · line 1270 · raw

@book:Book -> @pp:Put -> @+pred:U32 -> @arr:Array<U32> -> @zig:List<&2, U32> -> Pair(Book, Pair(U32, Put))

DC at the first zigzag index, then the AC run, both read from the frequency plane

def encode.chan.flat source · line 1277 · raw

@plane:Array<U32> -> @book:Book -> @pp:Put -> @+pred:U32 -> @+kern:Kern -> @+zig:List<&2, U32> -> @+qs:List<&2, U32> -> Pair(Book, Pair(U32, Put))

def encode.chan.mix source · line 1292 · raw

@plane:Array<U32> -> @book:Book -> @pp:Put -> @+pred:U32 -> @+kern:Kern -> @zig:List<&2, U32> -> @qs:List<&2, U32> -> Pair(Book, Pair(U32, Put))

def encode.chan.go source · line 1304 · raw

@flat:Bool -> @plane:Array<U32> -> @book:Book -> @pp:Put -> @+pred:U32 -> @+kern:Kern -> @+zig:List<&2, U32> -> @+qs:List<&2, U32> -> Pair(Book, Pair(U32, Put))

def encode.pix.at source · line 1320 · raw

@aa:Array<U32> -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> Pair(Array<U32>, U32)

def encode.tile.store source · line 1323 · raw

@got:Pair(Array<U32>, U32) -> @ys:Array<U32> -> @bs:Array<U32> -> @rs:Array<U32> -> @fy:Bool -> @fb:Bool -> @fr:Bool -> @+ii:U32 -> Tile

def encode.tile.pix source · line 1344 · raw

@tt:Tile -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> @+ii:U32 -> Tile

def encode.tile.row source · line 1349 · raw

@left:Nat -> @tt:Tile -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> @+ii:U32 -> Tile

def encode.tile.rows source · line 1357 · raw

@left:Nat -> @tt:Tile -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> @+ii:U32 -> Tile

def encode.tile source · line 1366 · raw

@aa:Array<U32> -> @+ww:U32 -> @+hh:U32 -> @+xx:U32 -> @+yy:U32 -> Tile

three level planes from one walk of this MCU

def encode.chan.pair source · line 1370 · raw

@got:Pair(Book, Pair(U32, Put)) -> @aa:Array<U32> -> Pair(Array<U32>, Pair(Book, Pair(U32, Put)))

def encode.step.cr source · line 1375 · raw

@got:Pair(Book, Pair(U32, Put)) -> @aa:Array<U32> -> @rs:Array<U32> -> @fr:Bool -> @+ydc:U32 -> @+pr:U32 -> @+kern:Kern -> @+zig:List<&2, U32> -> @+qs:List<&2, U32> -> Pair(Array<U32>, Pair(Book, Pair(U32, Pair(U32, Pair(U32, Put)))))

def encode.step.cb source · line 1389 · raw

@got:Pair(Book, Pair(U32, Put)) -> @aa:Array<U32> -> @bs:Array<U32> -> @rs:Array<U32> -> @fb:Bool -> @fr:Bool -> @+pb:U32 -> @+pr:U32 -> @+kern:Kern -> @+zig:List<&2, U32> -> @+qs:List<&2, U32> -> Pair(Array<U32>, Pair(Book, Pair(U32, Pair(U32, Pair(U32, Put)))))

def encode.step.tile source · line 1405 · raw

@tt:Tile -> @book:Book -> @bit:Put -> @+py:U32 -> @+pb:U32 -> @+pr:U32 -> @+kern:Kern -> @+zig:List<&2, U32> -> @+qs:List<&2, U32> -> Pair(Array<U32>, Pair(Book, Pair(U32, Pair(U32, Pair(U32, Put)))))

def encode.step.y source · line 1421 · raw

@aa:Array<U32> -> @book:Book -> @bit:Put -> @+py:U32 -> @+pb:U32 -> @+pr:U32 -> @+xx:U32 -> @+yy:U32 -> @+ww:U32 -> @+hh:U32 -> @+ctx:Ctx -> Pair(Array<U32>, Pair(Book, Pair(U32, Pair(U32, Pair(U32, Put)))))

one walk of the MCU, then Y, Cb, and Cr. Each clipped DC is the next predictor.

def encode.mcus.done source · line 1438 · raw

@got:Pair(Array<U32>, Pair(Book, Pair(U32, Pair(U32, Pair(U32, Put))))) -> Put

def encode.mcus source · line 1445 · raw

@left:Nat -> @got:Pair(Array<U32>, Pair(Book, Pair(U32, Pair(U32, Pair(U32, Put))))) -> @+mx:U32 -> @+my:U32 -> @+mw:U32 -> @+ww:U32 -> @+hh:U32 -> @+ctx:Ctx -> Put

def encode.finish source · line 1466 · raw

@+ww:U32 -> @+hh:U32 -> @ent:List<&2, U32> -> @+dcc:List<&2, U32> -> @+dcs:List<&2, U32> -> @+acc:List<&2, U32> -> @+acs:List<&2, U32> -> List<&2, U32>

def encode.hunt.nil source · line 1478 · raw

@+seen:U32 -> Pair(Bool, List<&2, U32>)

samples before a mismatch were packed 8421504, so that prefix can be replayed

def encode.hunt.mix source · line 1481 · raw

@+seen:U32 -> @+hh:U32 -> @tt:List<&2, U32> -> Pair(Bool, List<&2, U32>)

def encode.hunt.rest source · line 1484 · raw

@xs:List<&2, U32> -> Pair(Bool, List<&2, U32>)

def encode.hunt.done source · line 1489 · raw

@eq:Bool -> @+hh:U32 -> @xs:List<&2, U32> -> @+seen:U32 -> Pair(Bool, List<&2, U32>)

no samples remain after h

def encode.hunt.empty source · line 1497 · raw

@eq:Bool -> @+hh:U32 -> @+seen:U32 -> Pair(Bool, List<&2, U32>)

the list ended before the area did

def encode.hunt.go source · line 1505 · raw

@left:Nat -> @xs:List<&2, U32> -> @eq:Bool -> @+hh:U32 -> @+seen:U32 -> Pair(Bool, List<&2, U32>)

h was just read. left samples remain after it. seen neutral samples precede it.

def encode.hold.no source · line 1521 · raw

@+seen:U32 -> @+cc:U32 -> @tail:List<&2, U32> -> Pair(Bool, List<&2, U32>)

the area was one colour. seen of those samples are replayed only on a miss.

def encode.hold.yes source · line 1524 · raw

@xs:List<&2, U32> -> @+cc:U32 -> @+hh:U32 -> @+seen:U32 -> Pair(Bool, List<&2, U32>)

def encode.hold.done source · line 1529 · raw

@eq:Bool -> @+hh:U32 -> @xs:List<&2, U32> -> @+cc:U32 -> @+seen:U32 -> Pair(Bool, List<&2, U32>)

def encode.hold.empty source · line 1536 · raw

@eq:Bool -> @+hh:U32 -> @+cc:U32 -> @+seen:U32 -> Pair(Bool, List<&2, U32>)

def encode.hold.go source · line 1545 · raw

@left:Nat -> @xs:List<&2, U32> -> @eq:Bool -> @+hh:U32 -> @+cc:U32 -> @+seen:U32 -> Pair(Bool, List<&2, U32>)

h was just read. left samples remain after it. seen copies of c precede it.

def encode.watch.neu source · line 1561 · raw

@got:Pair(Bool, List<&2, U32>) -> Pair(U32, Pair(U32, List<&2, U32>))

0 neutral, 1 one other colour, 2 mixed. A short list stays mixed: the plane's gaps are 0.

def encode.watch.sol source · line 1570 · raw

@got:Pair(Bool, List<&2, U32>) -> @+hh:U32 -> Pair(U32, Pair(U32, List<&2, U32>))

def encode.watch.pick source · line 1580 · raw

@neu:Bool -> @kk:Nat -> @tt:List<&2, U32> -> @+hh:U32 -> Pair(U32, Pair(U32, List<&2, U32>))

def encode.watch source · line 1587 · raw

@left:Nat -> @xs:List<&2, U32> -> Pair(U32, Pair(U32, List<&2, U32>))

def encode.pack.byte source · line 1600 · raw

@+buf:U32 -> @+len:U32 -> @+code:U32 -> U32

low len bits of code join the open byte; a finished byte is stuffed

def encode.pack.full source · line 1603 · raw

@full:Bool -> @out:List<&2, U32> -> @+buf:U32 -> @+nn:U32 -> @+len:U32 -> @+code:U32 -> Put

def encode.pack.keep source · line 1610 · raw

@pp:Put -> @+lo:U32 -> @+rest:U32 -> Put

def encode.pack.span source · line 1615 · raw

@out:List<&2, U32> -> @+buf:U32 -> @+nn:U32 -> @+len:U32 -> @+code:U32 -> Put

def encode.pack.fit source · line 1621 · raw

@fit:Bool -> @out:List<&2, U32> -> @+buf:U32 -> @+nn:U32 -> @+len:U32 -> @+code:U32 -> Put

def encode.pack source · line 1628 · raw

@pp:Put -> @+len:U32 -> @+code:U32 -> Put

def encode.zput source · line 1634 · raw

@pp:Put -> Put

Annex K luminance: a zero block is DC 00 then EOB 1010

def encode.ztri source · line 1637 · raw

@pp:Put -> Put

def encode.neutral.mcus source · line 1640 · raw

@left:Nat -> @pp:Put -> Put

def encode.neutral source · line 1648 · raw

@+ww:U32 -> @+hh:U32 -> List<&2, U32>

the entropy-coded bytes of a neutral picture: every MCU is three zero blocks

def encode.solid.end source · line 1654 · raw

@got:Pair(Array<U32>, Pair(Book, Pair(U32, Put))) -> Put

the rest of a solid picture is the same DC, so each later block is a zero difference

def encode.solid.cr source · line 1661 · raw

@got:Pair(Array<U32>, Pair(Book, Pair(U32, Put))) -> @+ctx:Ctx -> Put

def encode.solid.cb source · line 1666 · raw

@got:Pair(Array<U32>, Pair(Book, Pair(U32, Put))) -> @+ctx:Ctx -> Put

def encode.solid.y source · line 1671 · raw

@aa:Array<U32> -> @book:Book -> @bit:Put -> @+ctx:Ctx -> Put

def encode.solid source · line 1676 · raw

@+ww:U32 -> @+hh:U32 -> @+color:U32 -> List<&2, U32>

one colour repeated. The first MCU is that block; every later MCU is three zero differences. The result is the entropy-coded bytes.

def encode.go source · line 1686 · raw

@+ww:U32 -> @+hh:U32 -> @px:List<&2, U32> -> List<&2, U32>

the entropy-coded bytes of any other picture, MCU by MCU

def encode.dispatch.b source · line 1696 · raw

@neu:Bool -> @sol:Bool -> @+color:U32 -> @px:List<&2, U32> -> @+ww:U32 -> @+hh:U32 -> List<&2, U32>

the entropy-coded bytes, by what the watch found: neutral, one solid colour, or anything else

def encode.dispatch source · line 1713 · raw

@+tag:U32 -> @+color:U32 -> @px:List<&2, U32> -> @+ww:U32 -> @+hh:U32 -> List<&2, U32>

the watch's tag, 0 neutral, 1 solid, 2 mixed, asked with U32.is_eq so a law reaches every tag

def encode.arm.use source · line 1717 · raw

@got:Pair(U32, Pair(U32, List<&2, U32>)) -> @+ww:U32 -> @+hh:U32 -> List<&2, U32>

the watch's answer, dispatched

def encode.arm source · line 1723 · raw

@+ww:U32 -> @+hh:U32 -> @px:List<&2, U32> -> List<&2, U32>

the entropy-coded bytes of an accepted size

def encode.pick source · line 1726 · raw

@bad:Bool -> @+ww:U32 -> @+hh:U32 -> @px:List<&2, U32> -> List<&2, U32>

def encode source · line 1736 · raw

@+ww:U32 -> @+hh:U32 -> @px:List<&2, U32> -> List<&2, U32>

baseline sequential 4:4:4 JPEG bytes for packed RGB samples, or SOI EOI when the size is rejected