diff options
author | =Corey Hulen <corey@hulen.com> | 2015-09-17 09:21:56 -0700 |
---|---|---|
committer | =Corey Hulen <corey@hulen.com> | 2015-09-17 09:21:56 -0700 |
commit | 9b5198c69c01ff11b0977282b61cf2222ef2f2b1 (patch) | |
tree | a5179226ff013f082a6629dbf888454c53611383 /Godeps/_workspace/src/golang.org/x/image/font/plan9font/plan9font.go | |
parent | 51c3445e694e68fdecd809f17fbaa64751e5931c (diff) | |
parent | 3edd8dbdf65e0b7be422a46328cb85482aa78f69 (diff) | |
download | chat-9b5198c69c01ff11b0977282b61cf2222ef2f2b1.tar.gz chat-9b5198c69c01ff11b0977282b61cf2222ef2f2b1.tar.bz2 chat-9b5198c69c01ff11b0977282b61cf2222ef2f2b1.zip |
Merge branch 'master' into PLT-92
Diffstat (limited to 'Godeps/_workspace/src/golang.org/x/image/font/plan9font/plan9font.go')
-rw-r--r-- | Godeps/_workspace/src/golang.org/x/image/font/plan9font/plan9font.go | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/golang.org/x/image/font/plan9font/plan9font.go b/Godeps/_workspace/src/golang.org/x/image/font/plan9font/plan9font.go new file mode 100644 index 000000000..9c686b776 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/image/font/plan9font/plan9font.go @@ -0,0 +1,556 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package plan9font implements font faces for the Plan 9 font and subfont file +// formats. These formats are described at +// http://plan9.bell-labs.com/magic/man2html/6/font +package plan9font + +// TODO: have a subface use an *image.Alpha instead of plan9Image implementing +// the image.Image interface? The image/draw code has a fast path for +// *image.Alpha masks. + +import ( + "bytes" + "errors" + "fmt" + "image" + "image/color" + "log" + "strconv" + "strings" + + "golang.org/x/image/font" + "golang.org/x/image/math/fixed" +) + +// fontchar describes one character glyph in a subfont. +// +// For more detail, look for "struct Fontchar" in +// http://plan9.bell-labs.com/magic/man2html/2/cachechars +type fontchar struct { + x uint32 // X position in the image holding the glyphs. + top uint8 // First non-zero scan line. + bottom uint8 // Last non-zero scan line. + left int8 // Offset of baseline. + width uint8 // Width of baseline. +} + +func parseFontchars(p []byte) []fontchar { + fc := make([]fontchar, len(p)/6) + for i := range fc { + fc[i] = fontchar{ + x: uint32(p[0]) | uint32(p[1])<<8, + top: uint8(p[2]), + bottom: uint8(p[3]), + left: int8(p[4]), + width: uint8(p[5]), + } + p = p[6:] + } + return fc +} + +// subface implements font.Face for a Plan 9 subfont. +type subface struct { + firstRune rune // First rune in the subfont. + n int // Number of characters in the subfont. + height int // Inter-line spacing. + ascent int // Height above the baseline. + fontchars []fontchar // Character descriptions. + img *plan9Image // Image holding the glyphs. +} + +func (f *subface) Close() error { return nil } +func (f *subface) Kern(r0, r1 rune) fixed.Int26_6 { return 0 } + +func (f *subface) Glyph(dot fixed.Point26_6, r rune) ( + dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { + + r -= f.firstRune + if r < 0 || f.n <= int(r) { + return image.Rectangle{}, nil, image.Point{}, 0, false + } + i := &f.fontchars[r+0] + j := &f.fontchars[r+1] + + minX := int(dot.X+32)>>6 + int(i.left) + minY := int(dot.Y+32)>>6 + int(i.top) - f.ascent + dr = image.Rectangle{ + Min: image.Point{ + X: minX, + Y: minY, + }, + Max: image.Point{ + X: minX + int(j.x-i.x), + Y: minY + int(i.bottom) - int(i.top), + }, + } + return dr, f.img, image.Point{int(i.x), int(i.top)}, fixed.Int26_6(i.width) << 6, true +} + +func (f *subface) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { + r -= f.firstRune + if r < 0 || f.n <= int(r) { + return fixed.Rectangle26_6{}, 0, false + } + i := &f.fontchars[r+0] + j := &f.fontchars[r+1] + + bounds = fixed.R( + int(i.left), + int(i.top)-f.ascent, + int(i.left)+int(j.x-i.x), + int(i.bottom)-f.ascent, + ) + return bounds, fixed.Int26_6(i.width) << 6, true +} + +func (f *subface) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { + r -= f.firstRune + if r < 0 || f.n <= int(r) { + return 0, false + } + return fixed.Int26_6(f.fontchars[r].width) << 6, true +} + +// runeRange maps a single rune range [lo, hi] to a lazily loaded subface. Both +// ends of the range are inclusive. +type runeRange struct { + lo, hi rune + offset rune // subfont index that the lo rune maps to. + relFilename string + subface *subface + bad bool +} + +// face implements font.Face for a Plan 9 font. +// +// It maps multiple rune ranges to *subface values. Rune ranges may overlap; +// the first match wins. +type face struct { + height int + ascent int + readFile func(relFilename string) ([]byte, error) + runeRanges []runeRange +} + +func (f *face) Close() error { return nil } +func (f *face) Kern(r0, r1 rune) fixed.Int26_6 { return 0 } + +func (f *face) Glyph(dot fixed.Point26_6, r rune) ( + dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { + + if s, rr := f.subface(r); s != nil { + return s.Glyph(dot, rr) + } + return image.Rectangle{}, nil, image.Point{}, 0, false +} + +func (f *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { + if s, rr := f.subface(r); s != nil { + return s.GlyphBounds(rr) + } + return fixed.Rectangle26_6{}, 0, false +} + +func (f *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { + if s, rr := f.subface(r); s != nil { + return s.GlyphAdvance(rr) + } + return 0, false +} + +func (f *face) subface(r rune) (*subface, rune) { + // Fall back on U+FFFD if we can't find r. + for _, rr := range [2]rune{r, '\ufffd'} { + // We have to do linear, not binary search. plan9port's + // lucsans/unicode.8.font says: + // 0x2591 0x2593 ../luc/Altshades.7.0 + // 0x2500 0x25ee ../luc/FormBlock.7.0 + // and the rune ranges overlap. + for i := range f.runeRanges { + x := &f.runeRanges[i] + if rr < x.lo || x.hi < rr || x.bad { + continue + } + if x.subface == nil { + data, err := f.readFile(x.relFilename) + if err != nil { + log.Printf("plan9font: couldn't read subfont %q: %v", x.relFilename, err) + x.bad = true + continue + } + sub, err := ParseSubfont(data, x.lo-x.offset) + if err != nil { + log.Printf("plan9font: couldn't parse subfont %q: %v", x.relFilename, err) + x.bad = true + continue + } + x.subface = sub.(*subface) + } + return x.subface, rr + } + } + return nil, 0 +} + +// ParseFont parses a Plan 9 font file. data is the contents of that font file, +// which gives relative filenames for subfont files. readFile returns the +// contents of those subfont files. It is similar to io/ioutil's ReadFile +// function, except that it takes a relative filename instead of an absolute +// one. +func ParseFont(data []byte, readFile func(relFilename string) ([]byte, error)) (font.Face, error) { + f := &face{ + readFile: readFile, + } + // TODO: don't use strconv, to avoid the conversions from []byte to string? + for first := true; len(data) > 0; first = false { + i := bytes.IndexByte(data, '\n') + if i < 0 { + return nil, errors.New("plan9font: invalid font: no final newline") + } + row := string(data[:i]) + data = data[i+1:] + if first { + height, s, ok := nextInt32(row) + if !ok { + return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row) + } + ascent, s, ok := nextInt32(s) + if !ok { + return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row) + } + if height < 0 || 0xffff < height || ascent < 0 || 0xffff < ascent { + return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row) + } + f.height, f.ascent = int(height), int(ascent) + continue + } + lo, s, ok := nextInt32(row) + if !ok { + return nil, fmt.Errorf("plan9font: invalid font: invalid row %q", row) + } + hi, s, ok := nextInt32(s) + if !ok { + return nil, fmt.Errorf("plan9font: invalid font: invalid row %q", row) + } + offset, s, _ := nextInt32(s) + + f.runeRanges = append(f.runeRanges, runeRange{ + lo: lo, + hi: hi, + offset: offset, + relFilename: s, + }) + } + return f, nil +} + +func nextInt32(s string) (ret int32, remaining string, ok bool) { + i := 0 + for ; i < len(s) && s[i] <= ' '; i++ { + } + j := i + for ; j < len(s) && s[j] > ' '; j++ { + } + n, err := strconv.ParseInt(s[i:j], 0, 32) + if err != nil { + return 0, s, false + } + for ; j < len(s) && s[j] <= ' '; j++ { + } + return int32(n), s[j:], true +} + +// ParseSubfont parses a Plan 9 subfont file. +// +// firstRune is the first rune in the subfont file. For example, the +// Phonetic.6.0 subfont, containing glyphs in the range U+0250 to U+02E9, would +// set firstRune to '\u0250'. +func ParseSubfont(data []byte, firstRune rune) (font.Face, error) { + data, m, err := parseImage(data) + if err != nil { + return nil, err + } + if len(data) < 3*12 { + return nil, errors.New("plan9font: invalid subfont: header too short") + } + n := atoi(data[0*12:]) + height := atoi(data[1*12:]) + ascent := atoi(data[2*12:]) + data = data[3*12:] + if len(data) != 6*(n+1) { + return nil, errors.New("plan9font: invalid subfont: data length mismatch") + } + return &subface{ + firstRune: firstRune, + n: n, + height: height, + ascent: ascent, + fontchars: parseFontchars(data), + img: m, + }, nil +} + +// plan9Image implements that subset of the Plan 9 image feature set that is +// used by this font file format. +// +// Some features, such as the repl bit and a clip rectangle, are omitted for +// simplicity. +type plan9Image struct { + depth int // Depth of the pixels in bits. + width int // Width in bytes of a single scan line. + rect image.Rectangle // Extent of the image. + pix []byte // Pixel bits. +} + +func (m *plan9Image) byteoffset(x, y int) int { + a := y * m.width + if m.depth < 8 { + // We need to always round down, but Go rounds toward zero. + np := 8 / m.depth + if x < 0 { + return a + (x-np+1)/np + } + return a + x/np + } + return a + x*(m.depth/8) +} + +func (m *plan9Image) Bounds() image.Rectangle { return m.rect } +func (m *plan9Image) ColorModel() color.Model { return color.AlphaModel } + +func (m *plan9Image) At(x, y int) color.Color { + if (image.Point{x, y}).In(m.rect) { + b := m.pix[m.byteoffset(x, y)] + switch m.depth { + case 1: + // CGrey, 1. + mask := uint8(1 << uint8(7-x&7)) + if (b & mask) != 0 { + return color.Alpha{0xff} + } + return color.Alpha{0x00} + case 2: + // CGrey, 2. + shift := uint(x&3) << 1 + // Place pixel at top of word. + y := b << shift + y &= 0xc0 + // Replicate throughout. + y |= y >> 2 + y |= y >> 4 + return color.Alpha{y} + } + } + return color.Alpha{0x00} +} + +var compressed = []byte("compressed\n") + +func parseImage(data []byte) (remainingData []byte, m *plan9Image, retErr error) { + if !bytes.HasPrefix(data, compressed) { + return nil, nil, errors.New("plan9font: unsupported uncompressed format") + } + data = data[len(compressed):] + + const hdrSize = 5 * 12 + if len(data) < hdrSize { + return nil, nil, errors.New("plan9font: invalid image: header too short") + } + hdr, data := data[:hdrSize], data[hdrSize:] + + // Distinguish new channel descriptor from old ldepth. Channel descriptors + // have letters as well as numbers, while ldepths are a single digit + // formatted as %-11d. + new := false + for m := 0; m < 10; m++ { + if hdr[m] != ' ' { + new = true + break + } + } + if hdr[11] != ' ' { + return nil, nil, errors.New("plan9font: invalid image: bad header") + } + if !new { + return nil, nil, errors.New("plan9font: unsupported ldepth format") + } + + depth := 0 + switch s := strings.TrimSpace(string(hdr[:1*12])); s { + default: + return nil, nil, fmt.Errorf("plan9font: unsupported pixel format %q", s) + case "k1": + depth = 1 + case "k2": + depth = 2 + } + r := ator(hdr[1*12:]) + if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y { + return nil, nil, errors.New("plan9font: invalid image: bad rectangle") + } + + width := bytesPerLine(r, depth) + m = &plan9Image{ + depth: depth, + width: width, + rect: r, + pix: make([]byte, width*r.Dy()), + } + + miny := r.Min.Y + for miny != r.Max.Y { + if len(data) < 2*12 { + return nil, nil, errors.New("plan9font: invalid image: data band too short") + } + maxy := atoi(data[0*12:]) + nb := atoi(data[1*12:]) + data = data[2*12:] + + if len(data) < nb { + return nil, nil, errors.New("plan9font: invalid image: data band length mismatch") + } + buf := data[:nb] + data = data[nb:] + + if maxy <= miny || r.Max.Y < maxy { + return nil, nil, fmt.Errorf("plan9font: bad maxy %d", maxy) + } + // An old-format image would flip the bits here, but we don't support + // the old format. + rr := r + rr.Min.Y = miny + rr.Max.Y = maxy + if err := decompress(m, rr, buf); err != nil { + return nil, nil, err + } + miny = maxy + } + return data, m, nil +} + +// Compressed data are sequences of byte codes. If the first byte b has the +// 0x80 bit set, the next (b^0x80)+1 bytes are data. Otherwise, these two bytes +// specify a previous string to repeat. +const ( + compShortestMatch = 3 // shortest match possible. + compWindowSize = 1024 // window size. +) + +var ( + errDecompressBufferTooSmall = errors.New("plan9font: decompress: buffer too small") + errDecompressPhaseError = errors.New("plan9font: decompress: phase error") +) + +func decompress(m *plan9Image, r image.Rectangle, data []byte) error { + if !r.In(m.rect) { + return errors.New("plan9font: decompress: bad rectangle") + } + bpl := bytesPerLine(r, m.depth) + mem := make([]byte, compWindowSize) + memi := 0 + omemi := -1 + y := r.Min.Y + linei := m.byteoffset(r.Min.X, y) + eline := linei + bpl + datai := 0 + for { + if linei == eline { + y++ + if y == r.Max.Y { + break + } + linei = m.byteoffset(r.Min.X, y) + eline = linei + bpl + } + if datai == len(data) { + return errDecompressBufferTooSmall + } + c := data[datai] + datai++ + if c >= 128 { + for cnt := c - 128 + 1; cnt != 0; cnt-- { + if datai == len(data) { + return errDecompressBufferTooSmall + } + if linei == eline { + return errDecompressPhaseError + } + m.pix[linei] = data[datai] + linei++ + mem[memi] = data[datai] + memi++ + datai++ + if memi == len(mem) { + memi = 0 + } + } + } else { + if datai == len(data) { + return errDecompressBufferTooSmall + } + offs := int(data[datai]) + ((int(c) & 3) << 8) + 1 + datai++ + if memi < offs { + omemi = memi + (compWindowSize - offs) + } else { + omemi = memi - offs + } + for cnt := (c >> 2) + compShortestMatch; cnt != 0; cnt-- { + if linei == eline { + return errDecompressPhaseError + } + m.pix[linei] = mem[omemi] + linei++ + mem[memi] = mem[omemi] + memi++ + omemi++ + if omemi == len(mem) { + omemi = 0 + } + if memi == len(mem) { + memi = 0 + } + } + } + } + return nil +} + +func ator(b []byte) image.Rectangle { + return image.Rectangle{atop(b), atop(b[2*12:])} +} + +func atop(b []byte) image.Point { + return image.Pt(atoi(b), atoi(b[12:])) +} + +func atoi(b []byte) int { + i := 0 + for ; i < len(b) && b[i] == ' '; i++ { + } + n := 0 + for ; i < len(b) && '0' <= b[i] && b[i] <= '9'; i++ { + n = n*10 + int(b[i]) - '0' + } + return n +} + +func bytesPerLine(r image.Rectangle, depth int) int { + if depth <= 0 || 32 < depth { + panic("invalid depth") + } + var l int + if r.Min.X >= 0 { + l = (r.Max.X*depth + 7) / 8 + l -= (r.Min.X * depth) / 8 + } else { + // Make positive before divide. + t := (-r.Min.X*depth + 7) / 8 + l = t + (r.Max.X*depth+7)/8 + } + return l +} |