9fans.net/go@v0.0.5/draw/memdraw/write.go (about) 1 package memdraw 2 3 import ( 4 "fmt" 5 "os" 6 7 "9fans.net/go/draw" 8 ) 9 10 const ( 11 _CHUNK = 16000 12 _HSHIFT = 3 /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */ 13 _NHASH = 1 << (_HSHIFT * _NMATCH) 14 _HMASK = _NHASH - 1 15 ) 16 17 func hupdate(h uint32, c uint8) uint32 { 18 return ((h << _HSHIFT) ^ uint32(c)) & _HMASK 19 } 20 21 type hlist struct { 22 s int // pointer into data 23 next *hlist 24 prev *hlist 25 } 26 27 func writememimage(fd *os.File, i *Image) error { 28 r := i.R 29 bpl := draw.BytesPerLine(r, i.Depth) 30 n := r.Dy() * bpl 31 data := make([]byte, n) 32 ncblock := compblocksize(r, i.Depth) 33 outbuf := make([]byte, ncblock) 34 hash := make([]hlist, _NHASH) 35 chain := make([]hlist, _NMEM) 36 var dy int 37 for miny := r.Min.Y; miny != r.Max.Y; miny += dy { 38 dy = r.Max.Y - miny 39 if dy*bpl > _CHUNK { 40 dy = _CHUNK / bpl 41 } 42 nb, err := unloadmemimage(i, draw.Rect(r.Min.X, miny, r.Max.X, miny+dy), data[(miny-r.Min.Y)*bpl:]) 43 if err != nil { 44 return err 45 } 46 if nb != dy*bpl { 47 return fmt.Errorf("unloadmemimage phase error") 48 } 49 } 50 hdr := []byte(fmt.Sprintf("compressed\n%11s %11d %11d %11d %11d ", 51 i.Pix.String(), r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)) 52 if _, err := fd.Write(hdr); err != nil { 53 return err 54 } 55 56 edata := n 57 eout := ncblock 58 line := 0 // index into data 59 r.Max.Y = r.Min.Y 60 for line != edata { 61 for i := range hash { 62 hash[i] = hlist{} 63 } 64 for i := range chain { 65 chain[i] = hlist{} 66 } 67 cp := 0 // index into chain 68 h := uint32(0) 69 outp := 0 // index into outbuf 70 for n = 0; n != _NMATCH; n++ { 71 h = hupdate(h, data[line+n]) 72 } 73 loutp := 0 // index into outbuf 74 for line != edata { 75 ndump := 0 76 eline := line + bpl 77 var dumpbuf [_NDUMP]uint8 /* dump accumulator */ 78 for p := line; p != eline; { 79 var es int 80 if eline-p < _NRUN { 81 es = eline 82 } else { 83 es = p + _NRUN 84 } 85 var q int 86 runlen := 0 87 for hp := hash[h].next; hp != nil; hp = hp.next { 88 s := p + runlen 89 if s >= es { 90 continue 91 } 92 t := hp.s + runlen 93 for ; s >= p; s-- { 94 t0 := t 95 t-- 96 if data[s] != data[t0] { 97 goto matchloop 98 } 99 } 100 t += runlen + 2 101 s += runlen + 2 102 for ; s < es; s++ { 103 t0 := t 104 t++ 105 if data[s] != data[t0] { 106 break 107 } 108 } 109 n = s - p 110 if n > runlen { 111 runlen = n 112 q = hp.s 113 if n == _NRUN { 114 break 115 } 116 } 117 matchloop: 118 } 119 if runlen < _NMATCH { 120 if ndump == _NDUMP { 121 if eout-outp < ndump+1 { 122 goto Bfull 123 } 124 outbuf[outp] = uint8(ndump - 1 + 128) 125 outp++ 126 copy(outbuf[outp:outp+ndump], dumpbuf[:ndump]) 127 outp += ndump 128 ndump = 0 129 } 130 dumpbuf[ndump] = data[p] 131 ndump++ 132 runlen = 1 133 } else { 134 if ndump != 0 { 135 if eout-outp < ndump+1 { 136 goto Bfull 137 } 138 outbuf[outp] = uint8(ndump - 1 + 128) 139 outp++ 140 copy(outbuf[outp:outp+ndump], dumpbuf[:ndump]) 141 outp += ndump 142 ndump = 0 143 } 144 offs := p - q - 1 145 if eout-outp < 2 { 146 goto Bfull 147 } 148 outbuf[outp] = byte(((runlen - _NMATCH) << 2) + (offs >> 8)) 149 outp++ 150 outbuf[outp] = uint8(offs & 255) 151 outp++ 152 } 153 for q = p + runlen; p != q; p++ { 154 if chain[cp].prev != nil { 155 chain[cp].prev.next = nil 156 } 157 chain[cp].next = hash[h].next 158 chain[cp].prev = &hash[h] 159 if chain[cp].next != nil { 160 chain[cp].next.prev = &chain[cp] 161 } 162 chain[cp].prev.next = &chain[cp] 163 chain[cp].s = p 164 cp++ 165 if cp == _NMEM { 166 cp = 0 167 } 168 if edata-p > _NMATCH { 169 h = hupdate(h, data[p+_NMATCH]) 170 } 171 } 172 } 173 if ndump != 0 { 174 if eout-outp < ndump+1 { 175 goto Bfull 176 } 177 outbuf[outp] = uint8(ndump - 1 + 128) 178 outp++ 179 copy(outbuf[outp:outp+ndump], dumpbuf[:ndump]) 180 outp += ndump 181 } 182 line = eline 183 loutp = outp 184 r.Max.Y++ 185 } 186 Bfull: 187 if loutp == 0 { 188 return fmt.Errorf("no data") 189 } 190 n = loutp 191 hdr := []byte(fmt.Sprintf("%11d %11ld ", r.Max.Y, n)) 192 fd.Write(hdr) 193 fd.Write(outbuf[:n]) 194 r.Min.Y = r.Max.Y 195 } 196 return nil 197 }