func tupleTypeToFormat(tt string) Format {
	switch tt {
	case "BLACKANDWHITE":
		return PBM
	case "GRAYSCALE":
		return PGM
	case "RGB":
		return PPM
	default:
		return PAM
	}
}
tupleTypeToFormat maps a tuple-type string to a Netpbm format.
package netpbm
import (
	"bufio"
	"errors"
	"fmt"
	"image"
	"image/color"
	"io"
	"strings"
	"unicode"
	"github.com/spakin/netpbm/npcolor"
)
type netpbmReader struct {
	*bufio.Reader        
	err           error  
}
func newNetpbmReader(r *bufio.Reader) *netpbmReader {
	return &netpbmReader{Reader: r}
}
func (nr netpbmReader) Err() error {
	return nr.err
}
func (nr *netpbmReader) GetNextByteAsRune() rune {
	if nr.err != nil {
		return 0
	}
	var b byte
	b, nr.err = nr.ReadByte()
	if nr.err != nil {
		return 0
	}
	return rune(b)
}
func (nr *netpbmReader) GetLineAsKeyValue() []string {
	if nr.err != nil {
		return nil
	}
	var s string
	s, nr.err = nr.ReadString('\n')
	if nr.err != nil {
		return nil
	}
	s = strings.TrimSpace(s)
	if s == "" {
		return nil
	}
	if s[0] == '#' {
		return []string{"#", strings.TrimSpace(s[1:])}
	}
	fs := strings.SplitN(s, " ", 2)
	if len(fs) == 1 {
		return []string{fs[0], ""}
	}
	return []string{fs[0], strings.TrimSpace(fs[1])}
}
func (nr *netpbmReader) GetNextInt() int {
	var c rune
	for nr.err == nil && !unicode.IsDigit(c) {
		for c = nr.GetNextByteAsRune(); unicode.IsSpace(c); c = nr.GetNextByteAsRune() {
		}
		if c == '#' {
			for c = nr.GetNextByteAsRune(); c != '\n'; c = nr.GetNextByteAsRune() {
			}
		}
	}
	if nr.err != nil {
		return -1
	}
	value := int(c - '0')
	for c = nr.GetNextByteAsRune(); unicode.IsDigit(c); c = nr.GetNextByteAsRune() {
		value = value*10 + int(c-'0')
	}
	if nr.err != nil && nr.err != io.EOF {
		return -1
	}
	nr.err = nr.UnreadByte()
	if nr.err != nil {
		return -1
	}
	return value
}
func (nr *netpbmReader) GetIntsAndComments(n int) ([]int, []string, error) {
	num := 0                          
	numbers := make([]int, 0, n)      
	cmt := make([]rune, 0, 100)       
	comments := make([]string, 0, 1)  
	const (
		InSpace = iota
		InDigit
		InComment
	)
	state := InSpace   
	var prevState int  
	var c rune
RuneLoop:
	for {
		switch state {
		case InSpace:
			for c = nr.GetNextByteAsRune(); unicode.IsSpace(c); c = nr.GetNextByteAsRune() {
			}
			if c >= '0' && c <= '9' {
				state = InDigit
				num = int(c - '0')
			} else if c == '#' {
				state = InComment
				prevState = InSpace
			} else if c == 0 {
				return nil, nil, errors.New("Unexpected EOF in Netpbm header")
			} else {
				return nil, nil, fmt.Errorf("Unexpected header character %q", c)
			}
		case InDigit:
			for c = nr.GetNextByteAsRune(); c >= '0' && c <= '9'; c = nr.GetNextByteAsRune() {
				num = num*10 + int(c-'0')
			}
			if unicode.IsSpace(c) {
				state = InSpace
				numbers = append(numbers, num)
				if len(numbers) == n {
					return numbers, comments, nil
				}
			} else if c == '#' {
				state = InComment
				prevState = InDigit
			} else if c == 0 {
				return nil, nil, errors.New("Unexpected EOF in Netpbm header")
			} else {
				return nil, nil, fmt.Errorf("Unexpected header character %q", c)
			}
		case InComment:
			for c = nr.GetNextByteAsRune(); c != '\n' && c != '\r'; c = nr.GetNextByteAsRune() {
				cmt = append(cmt, c)
			}
			if len(cmt) > 0 && unicode.IsSpace(cmt[0]) {
				cmt = cmt[1:]
			}
			comments = append(comments, string(cmt))
			cmt = cmt[:0]
			state = prevState
			prevState = InComment
		default:
			break RuneLoop
		}
	}
	return nil, nil, errors.New("Unexpected EOF in Netpbm header")
}
func (nr *netpbmReader) GetASCIIData(maxVal int, data []uint8) bool {
	if maxVal < 256 {
		for i := 0; i < len(data); i++ {
			val := nr.GetNextInt()
			switch {
			case nr.Err() != nil:
				return false
			case val < 0 || val > maxVal:
				return false
			default:
				data[i] = uint8(val)
			}
		}
	} else {
		for i := 0; i < len(data); i += 2 {
			val := nr.GetNextInt()
			switch {
			case nr.Err() != nil:
				return false
			case val < 0 || val > maxVal:
				return false
			default:
				data[i] = uint8(val >> 8)
				data[i+1] = uint8(val)
			}
		}
	}
	return true
}
type netpbmHeader struct {
	Magic     string    
	Width     int       
	Height    int       
	Depth     int       
	Maxval    int       
	TupleType string    
	Comments  []string  
}
func (nr *netpbmReader) getMagic(min, max rune) (string, bool) {
	rune1 := nr.GetNextByteAsRune()
	if rune1 != 'P' {
		return "", false
	}
	rune2 := nr.GetNextByteAsRune()
	if rune2 < min || rune2 > max {
		return "", false
	}
	if !unicode.IsSpace(nr.GetNextByteAsRune()) {
		return "", false
	}
	return string(rune1) + string(rune2), true
}
func (nr *netpbmReader) GetNetpbmHeader() (netpbmHeader, bool) {
	var header netpbmHeader
	var ok bool
	header.Magic, ok = nr.getMagic('1', '6')
	if !ok {
		return netpbmHeader{}, false
	}
	switch header.Magic {
	case "P1", "P4":
		header.Maxval = 1
		nums, comments, err := nr.GetIntsAndComments(2)
		if err != nil {
			return netpbmHeader{}, false
		}
		header.Width = nums[0]
		header.Height = nums[1]
		header.Comments = comments
	default:
		nums, comments, err := nr.GetIntsAndComments(3)
		if err != nil {
			return netpbmHeader{}, false
		}
		header.Width = nums[0]
		header.Height = nums[1]
		header.Maxval = nums[2]
		header.Comments = comments
	}
	if nr.Err() != nil || header.Maxval < 1 || header.Maxval > 65535 {
		return netpbmHeader{}, false
	}
	return header, true
}
type Image interface {
	image.Image                              
	MaxValue() uint16                        
	Format() Format                          
	HasAlpha() bool                          
	Opaque() bool                            
	PixOffset(x, y int) int                  
	Set(x, y int, c color.Color)             
	SubImage(r image.Rectangle) image.Image  
}
type Format int
const (
	PNM Format = iota  
	PBM                
	PGM                
	PPM                
	PAM                
)
func (f Format) String() string {
	switch f {
	case PNM:
		return "PNM"
	case PBM:
		return "PBM"
	case PGM:
		return "PGM"
	case PPM:
		return "PPM"
	case PAM:
		return "PAM"
	default:
		return fmt.Sprintf("%%!s(netpbm.Format=%d)", f)
	}
}
type DecodeOptions struct {
	Target      Format  
	Exact       bool    
	PBMMaxValue uint16  
}
func DecodeConfigWithComments(r io.Reader) (image.Config, []string, error) {
	rr, ok := r.(*bufio.Reader)
	if !ok {
		rr = bufio.NewReader(r)
	}
	magic, err := rr.Peek(2)
	if err != nil {
		return image.Config{}, nil, err
	}
	if magic[0] != 'P' {
		return image.Config{}, nil, errors.New("Not a Netpbm image")
	}
	switch magic[1] {
	case '1', '4':
		return decodeConfigPBMWithComments(rr)
	case '2', '5':
		return decodeConfigPGMWithComments(rr)
	case '3', '6':
		return decodeConfigPPMWithComments(rr)
	case '7':
		return decodeConfigPAMWithComments(rr)
	default:
		return image.Config{}, nil, fmt.Errorf("Unrecognized magic sequence %q", string(magic))
	}
}
func DecodeConfig(r io.Reader) (image.Config, error) {
	cfg, _, err := DecodeConfigWithComments(r)
	return cfg, err
}
func DecodeWithComments(r io.Reader, opts *DecodeOptions) (Image, []string, error) {
	rr, ok := r.(*bufio.Reader)
	if !ok {
		rr = bufio.NewReader(r)
	}
	magic, err := rr.Peek(2)
	if err != nil {
		return nil, nil, err
	}
	if magic[0] != 'P' {
		return nil, nil, errors.New("Not a Netpbm image")
	}
	var o DecodeOptions
	if opts != nil {
		o = *opts
	}
	if o.PBMMaxValue == 0 {
		o.PBMMaxValue = 255
	}
	if o.Exact && o.Target == PNM {
		return nil, nil, errors.New("Exact=true is incompatible with Target=PNM")
	}
	var img image.Image    
	var comments []string  
	switch magic[1] {
	case '1':
		if o.Exact && o.Target != PBM {
			return nil, nil, errors.New("PBM rejected by Decode options")
		}
		img, comments, err = decodePBMPlainWithComments(rr)
	case '2':
		if o.Exact && o.Target != PGM {
			return nil, nil, errors.New("PGM rejected by Decode options")
		}
		img, comments, err = decodePGMPlainWithComments(rr)
	case '3':
		if o.Exact && o.Target != PPM {
			return nil, nil, errors.New("PPM rejected by Decode options")
		}
		img, comments, err = decodePPMPlainWithComments(rr)
	case '4':
		if o.Exact && o.Target != PBM {
			return nil, nil, errors.New("PBM rejected by Decode options")
		}
		img, comments, err = decodePBMWithComments(rr)
	case '5':
		if o.Exact && o.Target != PGM {
			return nil, nil, errors.New("PGM rejected by Decode options")
		}
		img, comments, err = decodePGMWithComments(rr)
	case '6':
		if o.Exact && o.Target != PPM {
			return nil, nil, errors.New("PPM rejected by Decode options")
		}
		img, comments, err = decodePPMWithComments(rr)
	case '7':
		img, comments, err = decodePAMWithComments(rr)
		if err != nil {
			return nil, nil, err
		}
		if o.Exact && img.(Image).Format() != o.Target {
			return nil, nil, fmt.Errorf("%s-flavored PAM rejected by Decode options", img.(Image).Format())
		}
	default:
		return nil, nil, fmt.Errorf("Unrecognized magic sequence %q", string(magic))
	}
	if err != nil {
		return nil, nil, err
	}
	nimg := img.(Image)
	if o.Target == PAM {
		return nimg, comments, nil
	}
	if o.Target == PNM {
		if !nimg.HasAlpha() {
			return nimg, comments, nil
		}
		var ok bool
		nimg, ok = RemoveAlpha(nimg)
		if ok {
			return nimg, comments, nil
		}
		return nil, nil, errors.New("Failed to remove the alpha channel")
	}
	if nimg.Format() > o.Target {
		return nil, nil, fmt.Errorf("Cannot demote a %s image to a %s image", nimg.Format(), o.Target)
	}
	for nimg.Format() < o.Target {
		switch nimg.Format() {
		case PBM:
			mVal := o.PBMMaxValue
			if mVal < 256 {
				nimg = nimg.(*BW).PromoteToGrayM(uint8(mVal))
			} else {
				nimg = nimg.(*BW).PromoteToGrayM32(mVal)
			}
		case PGM:
			if nimg.MaxValue() < 256 {
				nimg = nimg.(*GrayM).PromoteToRGBM()
			} else {
				nimg = nimg.(*GrayM32).PromoteToRGBM64()
			}
		default:
			panic("Attempted to promote a format other than PBM or PGM")
		}
	}
	return nimg, comments, nil
}
func Decode(r io.Reader, opts *DecodeOptions) (Image, error) {
	img, _, err := DecodeWithComments(r, opts)
	return img, err
}
type EncodeOptions struct {
	Format    Format    
	MaxValue  uint16    
	Plain     bool      
	TupleType string    
	Comments  []string  
}
func inferTupleType(m color.Model) string {
	c := m.Convert(dummyColor{})
	r, g, b, a := c.RGBA()
	tt := "RGB"
	if r == g && g == b {
		if r == 0 || r == a {
			tt = "BLACKANDWHITE"
		} else {
			tt = "GRAYSCALE"
		}
	}
	if a < 0xffff {
		tt += "_ALPHA"
	}
	return tt
}