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  }