9fans.net/go@v0.0.5/draw/font.go (about)

     1  package draw
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"sync"
     7  	"unicode/utf8"
     8  )
     9  
    10  // A Font represents a font that may be used to draw on the display.
    11  // A Font is constructed by reading a font file that describes how to
    12  // create a full font from a collection of subfonts, each of which
    13  // covers a section of the Unicode code space.
    14  //
    15  // A Font is a set of character images, indexed by runes.
    16  // The images are organized into Subfonts, each containing the images
    17  // for a small, contiguous set of runes.
    18  //
    19  // Font defines two important dimension fields: ‘Ascent’,
    20  // the distance from the top of the highest character
    21  // (actually the top of the image holding all the characters) to the baseline,
    22  // and ‘Height’, the distance from the top of the highest character
    23  // to the bottom of the lowest character (and hence, the interline spacing).
    24  type Font struct {
    25  	Display *Display
    26  	Name    string // name, typically from file.
    27  	Height  int    // max height of image; interline spacing
    28  	Ascent  int    // top of image to baseline
    29  	Scale   int    // pixel scaling
    30  
    31  	namespec   string
    32  	mu         sync.Mutex // only used if Display == nil
    33  	width      int        // widest so far; used in caching only
    34  	age        uint32     // increasing counter; used for LUR
    35  	maxdepth   int        // maximum depth of all loaded subfonts
    36  	cache      []cacheinfo
    37  	subf       []cachesubf
    38  	sub        []*cachefont // as read from file
    39  	cacheimage *Image
    40  
    41  	// doubly linked list of fonts known to display
    42  	ondisplaylist bool
    43  	next          *Font
    44  	prev          *Font
    45  
    46  	// on hi-dpi systems, one of these is f and the other is the other-dpi version of f
    47  	lodpi *Font
    48  	hidpi *Font
    49  }
    50  
    51  func (f *Font) lock() {
    52  	if f.Display != nil {
    53  		f.Display.mu.Lock()
    54  	} else {
    55  		f.mu.Lock()
    56  	}
    57  }
    58  
    59  func (f *Font) unlock() {
    60  	if f.Display != nil {
    61  		f.Display.mu.Unlock()
    62  	} else {
    63  		f.mu.Unlock()
    64  	}
    65  }
    66  
    67  type cachefont struct {
    68  	min         rune
    69  	max         rune
    70  	offset      int
    71  	name        string
    72  	subfontname string
    73  }
    74  
    75  type cacheinfo struct {
    76  	x     uint16
    77  	width uint8
    78  	left  int8
    79  	value rune
    80  	age   uint32
    81  }
    82  
    83  type cachesubf struct {
    84  	age uint32
    85  	cf  *cachefont
    86  	f   *subfont
    87  }
    88  
    89  // A subfont is a set of images for a contiguous range of Unicode code points,
    90  // stored as a single image with the characters placed side-by-side on a
    91  // common baseline.
    92  //
    93  // The Bits image fills the rectangle (0, 0, w, Height), where w is the sum
    94  // of the horizontal extents (of non-zero pixels) for all characters.
    95  // The pixels to be displayed for character c are in the rectangle
    96  // (i.X, i.Top, next.X, i.Bottom) where i is Info[c] and next is Info[c+1].
    97  // When a character is displayed at Point p in an image,
    98  // the character rectangle is placed at (p.X+i.Left, p.Y)
    99  // and the next character of the string is displayed at (p.X+i.Width, p.Y).
   100  // The baseline of the characters is ‘ascent’ rows down from the top
   101  // of the subfont image.
   102  // The `info' array has n+1 elements, one each for characters 0 to n-1
   103  // plus an additional entry so the width of the last character can be calculated.
   104  // Thus the width, w, of the Image associated with a subfont s is
   105  // s.Info[s.N].X.
   106  type subfont struct {
   107  	Name   string     // name, typically the file from which it was read
   108  	N      int        // number of characters in the subfont.
   109  	Height int        // interline spacing
   110  	Ascent int        // height above the baseline
   111  	Info   []Fontchar // character descriptions
   112  	Bits   *Image     // image holding the glyphs
   113  	ref    int
   114  }
   115  
   116  // A Fontchar descibes one character glyph in a font (really a subfont).
   117  type Fontchar struct {
   118  	X      int   // x position in the image holding the glyphs.
   119  	Top    uint8 // first non-zero scan line.
   120  	Bottom uint8 // last non-zero scan line.
   121  	Left   int8  // offset of baseline.
   122  	Width  uint8 // width of baseline.
   123  }
   124  
   125  const (
   126  	/* starting values */
   127  	_LOG2NFCACHE = 6
   128  	_NFCACHE     = (1 << _LOG2NFCACHE) /* #chars cached */
   129  	_NFLOOK      = 5                   /* #chars to scan in cache */
   130  	_NFSUBF      = 2                   /* #subfonts to cache */
   131  	/* max value */
   132  	_MAXFCACHE = 1024 + _NFLOOK /* upper limit */
   133  	_MAXSUBF   = 50             /* generous upper limit */
   134  	/* deltas */
   135  	_DSUBF = 4
   136  	/* expiry ages */
   137  	_SUBFAGE  = 10000
   138  	_CACHEAGE = 10000
   139  )
   140  
   141  const pjw = 0 /* use NUL==pjw for invisible characters */
   142  
   143  func cachechars(f *Font, in *input, cp []uint16, max int) (n, wid int, subfontname string) {
   144  	var i int
   145  	//println("cachechars", i<max, in.done)
   146  Loop:
   147  	for ; i < max && !in.done; in.next() {
   148  		r := in.ch
   149  		var (
   150  			c, tc              *cacheinfo
   151  			a                  uint32
   152  			sh, esh, h, th, ld int
   153  		)
   154  
   155  		sh = (17 * int(r)) & (len(f.cache) - _NFLOOK - 1)
   156  		esh = sh + _NFLOOK
   157  		h = sh
   158  		for h < esh {
   159  			c = &f.cache[h]
   160  			if c.value == r && c.age > 0 {
   161  				goto Found
   162  			}
   163  			h++
   164  		}
   165  
   166  		/*
   167  		 * Not found; toss out oldest entry
   168  		 */
   169  		a = ^uint32(0)
   170  		th = sh
   171  		for th < esh {
   172  			tc = &f.cache[th]
   173  			if tc.age < a {
   174  				a = tc.age
   175  				h = th
   176  				c = tc
   177  			}
   178  			th++
   179  		}
   180  
   181  		if a != 0 && f.age-a < 500 { // kicking out too recent; resize
   182  			nc := 2*(len(f.cache)-_NFLOOK) + _NFLOOK
   183  			if nc <= _MAXFCACHE {
   184  				if i == 0 {
   185  					fontresize(f, f.width, nc, f.maxdepth)
   186  				}
   187  				// else flush first; retry will resize
   188  				break Loop
   189  			}
   190  		}
   191  
   192  		if c.age == f.age { // flush pending string output
   193  			break Loop
   194  		}
   195  
   196  		ld, subfontname = loadchar(f, r, c, h, i > 0)
   197  		if ld <= 0 {
   198  			if ld == 0 {
   199  				continue Loop
   200  			}
   201  			break Loop
   202  		}
   203  		c = &f.cache[h]
   204  
   205  	Found:
   206  		//println("FOUND")
   207  		wid += int(c.width)
   208  		c.age = f.age
   209  		cp[i] = uint16(h)
   210  		i++
   211  	}
   212  	return i, wid, subfontname
   213  }
   214  
   215  func agefont(f *Font) {
   216  	f.age++
   217  	if f.age == 65536 {
   218  		/*
   219  		 * Renormalize ages
   220  		 */
   221  		for i := range f.cache {
   222  			c := &f.cache[i]
   223  			if c.age > 0 {
   224  				c.age >>= 2
   225  				c.age++
   226  			}
   227  		}
   228  		for i := range f.subf {
   229  			s := &f.subf[i]
   230  			if s.age > 0 {
   231  				if s.age < _SUBFAGE && s.cf.name != "" {
   232  					/* clean up */
   233  					if f.Display == nil || s.f != f.Display.defaultSubfont {
   234  						s.f.free()
   235  					}
   236  					s.cf = nil
   237  					s.f = nil
   238  					s.age = 0
   239  				} else {
   240  					s.age >>= 2
   241  					s.age++
   242  				}
   243  			}
   244  		}
   245  		f.age = (65536 >> 2) + 1
   246  	}
   247  }
   248  
   249  func cf2subfont(cf *cachefont, f *Font) (*subfont, error) {
   250  	name := cf.subfontname
   251  	if name == "" {
   252  		depth := 0
   253  		if f.Display != nil {
   254  			if f.Display.ScreenImage != nil {
   255  				depth = f.Display.ScreenImage.Depth
   256  			}
   257  		} else {
   258  			depth = 8
   259  		}
   260  		name = subfontname(cf.name, f.Name, depth)
   261  		if name == "" {
   262  			return nil, fmt.Errorf("unknown subfont")
   263  		}
   264  		cf.subfontname = name
   265  	}
   266  	sf := lookupsubfont(f.Display, name)
   267  	return sf, nil
   268  }
   269  
   270  // return 1 if load succeeded, 0 if failed, -1 if must retry
   271  func loadchar(f *Font, r rune, c *cacheinfo, h int, noflush bool) (int, string) {
   272  	var (
   273  		i, oi, wid, top, bottom int
   274  		pic                     rune
   275  		fi                      []Fontchar
   276  		cf                      *cachefont
   277  		subf                    *cachesubf
   278  		b                       []byte
   279  	)
   280  
   281  	pic = r
   282  Again:
   283  	for i, cf = range f.sub {
   284  		if cf.min <= pic && pic <= cf.max {
   285  			goto Found
   286  		}
   287  	}
   288  TryPJW:
   289  	if pic != pjw {
   290  		pic = pjw
   291  		goto Again
   292  	}
   293  	return 0, ""
   294  
   295  Found:
   296  	/*
   297  	 * Choose exact or oldest
   298  	 */
   299  	oi = 0
   300  	for i := range f.subf {
   301  		subf = &f.subf[i]
   302  		if cf == subf.cf {
   303  			goto Found2
   304  		}
   305  		if subf.age < f.subf[oi].age {
   306  			oi = i
   307  		}
   308  	}
   309  	subf = &f.subf[oi]
   310  
   311  	if subf.f != nil {
   312  		if f.age-subf.age > _SUBFAGE || len(f.subf) > _MAXSUBF {
   313  			// ancient data; toss
   314  			subf.f.free()
   315  			subf.cf = nil
   316  			subf.f = nil
   317  			subf.age = 0
   318  		} else { // too recent; grow instead
   319  			of := f.subf
   320  			f.subf = make([]cachesubf, len(f.subf)+_DSUBF)
   321  			copy(f.subf, of)
   322  			subf = &f.subf[len(of)]
   323  		}
   324  	}
   325  
   326  	subf.age = 0
   327  	subf.cf = nil
   328  	subf.f, _ = cf2subfont(cf, f)
   329  	if subf.f == nil {
   330  		if cf.subfontname == "" {
   331  			goto TryPJW
   332  		}
   333  		return -1, cf.subfontname
   334  	}
   335  
   336  	subf.cf = cf
   337  	if subf.f.Ascent > f.Ascent && f.Display != nil {
   338  		/* should print something? this is a mistake in the font file */
   339  		/* must prevent c.top from going negative when loading cache */
   340  		d := subf.f.Ascent - f.Ascent
   341  		b := subf.f.Bits
   342  		b.draw(b.R, b, nil, b.R.Min.Add(Pt(0, d)))
   343  		b.draw(Rect(b.R.Min.X, b.R.Max.Y-d, b.R.Max.X, b.R.Max.Y), f.Display.Black, nil, b.R.Min)
   344  		for i := 0; i < subf.f.N; i++ {
   345  			t := int(subf.f.Info[i].Top) - d
   346  			if t < 0 {
   347  				t = 0
   348  			}
   349  			subf.f.Info[i].Top = uint8(t)
   350  			t = int(subf.f.Info[i].Bottom) - d
   351  			if t < 0 {
   352  				t = 0
   353  			}
   354  			subf.f.Info[i].Bottom = uint8(t)
   355  		}
   356  		subf.f.Ascent = f.Ascent
   357  	}
   358  
   359  Found2:
   360  	subf.age = f.age
   361  
   362  	/* possible overflow here, but works out okay */
   363  	pic += rune(cf.offset)
   364  	pic -= cf.min
   365  	if int(pic) >= subf.f.N {
   366  		goto TryPJW
   367  	}
   368  	fi = subf.f.Info[pic : pic+2]
   369  	if fi[0].Width == 0 {
   370  		goto TryPJW
   371  	}
   372  	wid = fi[1].X - fi[0].X
   373  	if f.width < wid || f.width == 0 || f.maxdepth < subf.f.Bits.Depth {
   374  		/*
   375  		 * Flush, free, reload (easier than reformatting f.b)
   376  		 */
   377  		if noflush {
   378  			return -1, ""
   379  		}
   380  		if f.width < wid {
   381  			f.width = wid
   382  		}
   383  		if f.maxdepth < subf.f.Bits.Depth {
   384  			f.maxdepth = subf.f.Bits.Depth
   385  		}
   386  		i = fontresize(f, f.width, len(f.cache), f.maxdepth)
   387  		if i <= 0 {
   388  			return i, ""
   389  		}
   390  		/* c is still valid as didn't reallocate f.cache */
   391  	}
   392  	c.value = r
   393  	top = int(fi[0].Top) + (f.Ascent - subf.f.Ascent)
   394  	bottom = int(fi[0].Bottom) + (f.Ascent - subf.f.Ascent)
   395  	c.width = fi[0].Width
   396  	c.x = uint16(h * int(f.width))
   397  	c.left = fi[0].Left
   398  	if f.Display == nil {
   399  		return 1, ""
   400  	}
   401  	f.Display.flush(false) /* flush any pending errors */
   402  	b = f.Display.bufimage(37)
   403  	b[0] = 'l'
   404  	bplong(b[1:], uint32(f.cacheimage.id))
   405  	bplong(b[5:], uint32(subf.f.Bits.id))
   406  	bpshort(b[9:], uint16(h))
   407  	bplong(b[11:], uint32(c.x))
   408  	bplong(b[15:], uint32(top))
   409  	bplong(b[19:], uint32(int(c.x)+int(fi[1].X-fi[0].X)))
   410  	bplong(b[23:], uint32(bottom))
   411  	bplong(b[27:], uint32(fi[0].X))
   412  	bplong(b[31:], uint32(fi[0].Top))
   413  	b[35] = byte(fi[0].Left)
   414  	b[36] = fi[0].Width
   415  	return 1, ""
   416  }
   417  
   418  // return whether resize succeeded && f.cache is unchanged
   419  func fontresize(f *Font, wid, ncache, depth int) int {
   420  	var (
   421  		ret int
   422  		new *Image
   423  		b   []byte
   424  		d   *Display
   425  		err error
   426  	)
   427  
   428  	if depth <= 0 {
   429  		depth = 1
   430  	}
   431  
   432  	d = f.Display
   433  	if d == nil {
   434  		goto Nodisplay
   435  	}
   436  	new, err = d.allocImage(Rect(0, 0, ncache*wid, f.Height), MakePix(CGrey, depth), false, 0)
   437  	if err != nil {
   438  		fmt.Fprintf(os.Stderr, "font cache resize failed\n")
   439  		panic("resize")
   440  	}
   441  	d.flush(false) // flush any pending errors
   442  	b = d.bufimage(1 + 4 + 4 + 1)
   443  	b[0] = 'i'
   444  	bplong(b[1:], new.id)
   445  	bplong(b[5:], uint32(ncache))
   446  	b[9] = byte(f.Ascent)
   447  	if err := d.flush(false); err != nil {
   448  		fmt.Fprintf(os.Stderr, "resize: init failed\n")
   449  		new.free()
   450  		goto Return
   451  	}
   452  	f.cacheimage.free()
   453  	f.cacheimage = new
   454  
   455  Nodisplay:
   456  	f.width = wid
   457  	f.maxdepth = depth
   458  	ret = 1
   459  	if len(f.cache) != ncache {
   460  		f.cache = make([]cacheinfo, ncache)
   461  	}
   462  
   463  Return:
   464  	for i := range f.cache {
   465  		f.cache[i] = cacheinfo{}
   466  	}
   467  	return ret
   468  }
   469  
   470  // An input can read a rune at a time from a string, []byte, or []rune.
   471  type input struct {
   472  	mode int
   473  	s    string
   474  	b    []byte
   475  	r    []rune
   476  	size int
   477  	ch   rune
   478  	done bool
   479  }
   480  
   481  func (in *input) init(s string, b []byte, r []rune) {
   482  	//println("init:", s)
   483  	in.s = s
   484  	in.b = b
   485  	in.r = r
   486  	in.mode = 0
   487  	if len(in.s) == 0 {
   488  		in.mode = 1
   489  		if len(in.b) == 0 {
   490  			in.mode = 2
   491  		}
   492  	}
   493  
   494  	in.next()
   495  }
   496  
   497  func (in *input) next() {
   498  	switch in.mode {
   499  	case 0:
   500  		in.s = in.s[in.size:]
   501  		if len(in.s) == 0 {
   502  			in.done = true
   503  			return
   504  		}
   505  		in.ch, in.size = utf8.DecodeRuneInString(in.s)
   506  	case 1:
   507  		in.b = in.b[in.size:]
   508  		if len(in.b) == 0 {
   509  			in.done = true
   510  			return
   511  		}
   512  		in.ch, in.size = utf8.DecodeRune(in.b)
   513  	case 2:
   514  		in.r = in.r[in.size:]
   515  		if len(in.r) == 0 {
   516  			in.done = true
   517  			return
   518  		}
   519  		in.ch = in.r[0]
   520  		in.size = 1
   521  	}
   522  	//println("next is ", in.ch, in.done)
   523  }