func (p *GrayAM) SetGrayAM(x, y int, c npcolor.GrayAM) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	if c.M == p.Model.M {
		p.Pix[i+0] = c.Y
		p.Pix[i+1] = c.A
	} else {
		p.Set(x, y, c)
	}
}
SetGrayAM sets the pixel at (x, y) to a given color, expressed as an
npcolor.GrayAM.
package netpbm
import (
	"bufio"
	"errors"
	"fmt"
	"image"
	"image/color"
	"io"
	"strconv"
	"strings"
	"github.com/spakin/netpbm/npcolor"
)
type pamTupleType int
const (
	pamBlackAndWhite pamTupleType = iota
	pamBlackAndWhiteAlpha
	pamGrayscale
	pamGrayscaleAlpha
	pamColor
	pamColorAlpha
)
var ttToInt = map[string]pamTupleType{
	"BLACKANDWHITE":       pamBlackAndWhite,
	"BLACKANDWHITE_ALPHA": pamBlackAndWhiteAlpha,
	"GRAYSCALE":           pamGrayscale,
	"GRAYSCALE_ALPHA":     pamGrayscaleAlpha,
	"RGB":                 pamColor,
	"RGB_ALPHA":           pamColorAlpha,
}
type RGBAM struct {
	Pix []uint8
	Stride int
	Rect image.Rectangle
	Model npcolor.RGBAMModel
}
func (p *RGBAM) ColorModel() color.Model { return p.Model }
func (p *RGBAM) Bounds() image.Rectangle { return p.Rect }
func (p *RGBAM) At(x, y int) color.Color {
	return p.RGBAMAt(x, y)
}
func (p *RGBAM) RGBAMAt(x, y int) npcolor.RGBAM {
	if !(image.Point{x, y}.In(p.Rect)) {
		return npcolor.RGBAM{}
	}
	i := p.PixOffset(x, y)
	return npcolor.RGBAM{
		R: p.Pix[i+0],
		G: p.Pix[i+1],
		B: p.Pix[i+2],
		A: p.Pix[i+3],
		M: p.Model.M,
	}
}
func (p *RGBAM) PixOffset(x, y int) int {
	return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
}
func (p *RGBAM) Set(x, y int, c color.Color) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	c1 := p.Model.Convert(c).(npcolor.RGBAM)
	p.Pix[i+0] = c1.R
	p.Pix[i+1] = c1.G
	p.Pix[i+2] = c1.B
	p.Pix[i+3] = c1.A
}
func (p *RGBAM) SetRGBAM(x, y int, c npcolor.RGBAM) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	if c.M == p.Model.M {
		p.Pix[i+0] = c.R
		p.Pix[i+1] = c.G
		p.Pix[i+2] = c.B
		p.Pix[i+3] = c.A
	} else {
		p.Set(x, y, c)
	}
}
func (p *RGBAM) SubImage(r image.Rectangle) image.Image {
	r = r.Intersect(p.Rect)
	if r.Empty() {
		return &RGBAM{}
	}
	i := p.PixOffset(r.Min.X, r.Min.Y)
	return &RGBAM{
		Pix:    p.Pix[i:],
		Stride: p.Stride,
		Rect:   r,
	}
}
func (p *RGBAM) Opaque() bool {
	if p.Rect.Empty() {
		return true
	}
	i0, i1 := 3, p.Rect.Dx()*4
	for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
		for i := i0; i < i1; i += 4 {
			if p.Pix[i] != 0xff {
				return false
			}
		}
		i0 += p.Stride
		i1 += p.Stride
	}
	return true
}
func (p *RGBAM) MaxValue() uint16 {
	return uint16(p.Model.M)
}
func (p *RGBAM) Format() Format {
	return PPM
}
func (p *RGBAM) HasAlpha() bool {
	return true
}
func NewRGBAM(r image.Rectangle, m uint8) *RGBAM {
	w, h := r.Dx(), r.Dy()
	pix := make([]uint8, 4*w*h)
	model := npcolor.RGBAMModel{M: m}
	return &RGBAM{pix, 4 * w, r, model}
}
type RGBAM64 struct {
	Pix []uint8
	Stride int
	Rect image.Rectangle
	Model npcolor.RGBAM64Model
}
func (p *RGBAM64) ColorModel() color.Model { return p.Model }
func (p *RGBAM64) Bounds() image.Rectangle { return p.Rect }
func (p *RGBAM64) At(x, y int) color.Color {
	return p.RGBAM64At(x, y)
}
func (p *RGBAM64) RGBAM64At(x, y int) npcolor.RGBAM64 {
	if !(image.Point{x, y}.In(p.Rect)) {
		return npcolor.RGBAM64{}
	}
	i := p.PixOffset(x, y)
	return npcolor.RGBAM64{
		R: uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1]),
		G: uint16(p.Pix[i+2])<<8 | uint16(p.Pix[i+3]),
		B: uint16(p.Pix[i+4])<<8 | uint16(p.Pix[i+5]),
		A: uint16(p.Pix[i+6])<<8 | uint16(p.Pix[i+7]),
		M: p.Model.M,
	}
}
func (p *RGBAM64) PixOffset(x, y int) int {
	return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8
}
func (p *RGBAM64) Set(x, y int, c color.Color) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	c1 := p.Model.Convert(c).(npcolor.RGBAM64)
	p.Pix[i+0] = uint8(c1.R >> 8)
	p.Pix[i+1] = uint8(c1.R)
	p.Pix[i+2] = uint8(c1.G >> 8)
	p.Pix[i+3] = uint8(c1.G)
	p.Pix[i+4] = uint8(c1.B >> 8)
	p.Pix[i+5] = uint8(c1.B)
	p.Pix[i+6] = uint8(c1.A >> 8)
	p.Pix[i+7] = uint8(c1.A)
}
func (p *RGBAM64) SetRGBAM64(x, y int, c npcolor.RGBAM64) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	if c.M == p.Model.M {
		p.Pix[i+0] = uint8(c.R >> 8)
		p.Pix[i+1] = uint8(c.R)
		p.Pix[i+2] = uint8(c.G >> 8)
		p.Pix[i+3] = uint8(c.G)
		p.Pix[i+4] = uint8(c.B >> 8)
		p.Pix[i+5] = uint8(c.B)
		p.Pix[i+6] = uint8(c.A >> 8)
		p.Pix[i+7] = uint8(c.A)
	} else {
		p.Set(x, y, c)
	}
}
func (p *RGBAM64) SubImage(r image.Rectangle) image.Image {
	r = r.Intersect(p.Rect)
	if r.Empty() {
		return &RGBAM64{}
	}
	i := p.PixOffset(r.Min.X, r.Min.Y)
	return &RGBAM64{
		Pix:    p.Pix[i:],
		Stride: p.Stride,
		Rect:   r,
	}
}
func (p *RGBAM64) Opaque() bool {
	if p.Rect.Empty() {
		return true
	}
	i0, i1 := 6, p.Rect.Dx()*8
	for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
		for i := i0; i < i1; i += 8 {
			if p.Pix[i+0] != 0xff || p.Pix[i+1] != 0xff {
				return false
			}
		}
		i0 += p.Stride
		i1 += p.Stride
	}
	return true
}
func (p *RGBAM64) MaxValue() uint16 {
	return uint16(p.Model.M)
}
func (p *RGBAM64) Format() Format {
	return PPM
}
func (p *RGBAM64) HasAlpha() bool {
	return true
}
func NewRGBAM64(r image.Rectangle, m uint16) *RGBAM64 {
	w, h := r.Dx(), r.Dy()
	pix := make([]uint8, 8*w*h)
	model := npcolor.RGBAM64Model{M: m}
	return &RGBAM64{pix, 8 * w, r, model}
}
func (nr *netpbmReader) GetPamHeader() (netpbmHeader, bool) {
	var header netpbmHeader
	var ok bool
	header.Magic, ok = nr.getMagic('7', '7')
	if !ok {
		return netpbmHeader{}, false
	}
ReadLoop:
	for {
		kv := nr.GetLineAsKeyValue()
		if nr.Err() != nil {
			return netpbmHeader{}, false
		}
		if len(kv) == 0 {
			continue
		}
		if len(kv) == 1 && kv[0] != "ENDHDR" {
			return netpbmHeader{}, false
		}
		var err error
		k, v := kv[0], kv[1]
		switch k {
		case "ENDHDR":
			break ReadLoop
		case "HEIGHT":
			header.Height, err = strconv.Atoi(v)
		case "WIDTH":
			header.Width, err = strconv.Atoi(v)
		case "DEPTH":
			header.Depth, err = strconv.Atoi(v)
		case "MAXVAL":
			header.Maxval, err = strconv.Atoi(v)
		case "TUPLTYPE":
			if header.TupleType != "" {
				header.TupleType += " "
			}
			header.TupleType += v
		case "#":
			header.Comments = append(header.Comments, v)
		default:
			return netpbmHeader{}, false
		}
		if err != nil {
			return netpbmHeader{}, false
		}
	}
	if header.Maxval < 1 || header.Maxval > 65535 {
		return netpbmHeader{}, false
	}
	return header, true
}
func decodeConfigPAMWithComments(r io.Reader) (image.Config, []string, error) {
	br, ok := r.(*bufio.Reader)
	if !ok {
		br = bufio.NewReader(r)
	}
	nr := newNetpbmReader(br)
	header, ok := nr.GetPamHeader()
	if !ok {
		err := nr.Err()
		if err == nil {
			err = errors.New("Invalid PAM header")
		}
		return image.Config{}, nil, err
	}
	var cfg image.Config
	cfg.Width = header.Width
	cfg.Height = header.Height
	ttype, ok := ttToInt[header.TupleType]
	if !ok {
		return image.Config{}, nil, fmt.Errorf("Unsupported tuple type %q", header.TupleType)
	}
	if header.Maxval < 256 {
		switch ttype {
		case pamColorAlpha:
			cfg.ColorModel = npcolor.RGBAMModel{M: uint8(header.Maxval)}
		case pamColor:
			cfg.ColorModel = npcolor.RGBMModel{M: uint8(header.Maxval)}
		case pamGrayscaleAlpha:
			cfg.ColorModel = npcolor.GrayAMModel{M: uint8(header.Maxval)}
		case pamGrayscale:
			cfg.ColorModel = npcolor.GrayMModel{M: uint8(header.Maxval)}
		case pamBlackAndWhiteAlpha:
			panic("Black & white + alpha is not currently supported")
		case pamBlackAndWhite:
			colorMap := make(color.Palette, 2)
			colorMap[0] = color.RGBA{0, 0, 0, 255}
			colorMap[1] = color.RGBA{255, 255, 255, 255}
			cfg.ColorModel = colorMap
		default:
			panic(fmt.Sprintf("Internal error processing tuple type %q", header.TupleType))
		}
	} else {
		switch ttype {
		case pamColorAlpha:
			cfg.ColorModel = npcolor.RGBAM64Model{M: uint16(header.Maxval)}
		case pamColor:
			cfg.ColorModel = npcolor.RGBM64Model{M: uint16(header.Maxval)}
		case pamGrayscaleAlpha:
			cfg.ColorModel = npcolor.GrayAM48Model{M: uint16(header.Maxval)}
		case pamGrayscale:
			cfg.ColorModel = npcolor.GrayM32Model{M: uint16(header.Maxval)}
		case pamBlackAndWhiteAlpha:
			panic("Black & white + alpha is not currently supported")
		case pamBlackAndWhite:
			colorMap := make(color.Palette, 2)
			colorMap[0] = color.RGBA{0, 0, 0, 255}
			colorMap[1] = color.RGBA{255, 255, 255, 255}
			cfg.ColorModel = colorMap
		default:
			panic(fmt.Sprintf("Internal error processing tuple type %q", header.TupleType))
		}
	}
	return cfg, header.Comments, nil
}
func decodeConfigPAM(r io.Reader) (image.Config, error) {
	img, _, err := decodeConfigPAMWithComments(r)
	return img, err
}
func decodePAMWithComments(r io.Reader) (image.Image, []string, error) {
	br := bufio.NewReader(r)
	config, comments, err := decodeConfigPAMWithComments(br)
	if err != nil {
		return nil, nil, err
	}
	var img image.Image  
	var data []uint8     
	var maxVal uint      
	switch model := config.ColorModel.(type) {
	case npcolor.RGBAMModel:
		maxVal = uint(model.M)
		pImg := NewRGBAM(image.Rect(0, 0, config.Width, config.Height), uint8(maxVal))
		data = pImg.Pix
		img = pImg
	case npcolor.RGBMModel:
		maxVal = uint(model.M)
		pImg := NewRGBM(image.Rect(0, 0, config.Width, config.Height), uint8(maxVal))
		data = pImg.Pix
		img = pImg
	case npcolor.GrayAMModel:
		maxVal = uint(model.M)
		pImg := NewGrayAM(image.Rect(0, 0, config.Width, config.Height), uint8(maxVal))
		data = pImg.Pix
		img = pImg
	case npcolor.GrayMModel:
		maxVal = uint(model.M)
		pImg := NewGrayM(image.Rect(0, 0, config.Width, config.Height), uint8(maxVal))
		data = pImg.Pix
		img = pImg
	case npcolor.RGBAM64Model:
		maxVal = uint(model.M)
		pImg := NewRGBAM64(image.Rect(0, 0, config.Width, config.Height), uint16(maxVal))
		data = pImg.Pix
		img = pImg
	case npcolor.RGBM64Model:
		maxVal = uint(model.M)
		pImg := NewRGBM64(image.Rect(0, 0, config.Width, config.Height), uint16(maxVal))
		data = pImg.Pix
		img = pImg
	case npcolor.GrayAM48Model:
		maxVal = uint(model.M)
		pImg := NewGrayAM48(image.Rect(0, 0, config.Width, config.Height), uint16(maxVal))
		data = pImg.Pix
		img = pImg
	case npcolor.GrayM32Model:
		maxVal = uint(model.M)
		pImg := NewGrayM32(image.Rect(0, 0, config.Width, config.Height), uint16(maxVal))
		data = pImg.Pix
		img = pImg
	default:
		panic("Unexpected color model")
	}
	for len(data) > 0 {
		nRead, err := br.Read(data)
		if err != nil && err != io.EOF {
			return img, nil, err
		}
		if nRead == 0 {
			return img, nil, errors.New("Failed to read binary PPM data")
		}
		data = data[nRead:]
	}
	return img, comments, nil
}
func decodePAM(r io.Reader) (image.Image, error) {
	img, _, err := decodePAMWithComments(r)
	return img, err
}
func init() {
	image.RegisterFormat("pam", "P7", decodePAM, decodeConfigPAM)
}
func encodePAM(w io.Writer, img image.Image, opts *EncodeOptions) error {
	var depth int
	ttype, ok := ttToInt[opts.TupleType]
	if !ok {
		return fmt.Errorf("Unsupported tuple type %q", opts.TupleType)
	}
	switch ttype {
	case pamColorAlpha:
		depth = 4
	case pamColor:
		depth = 3
	case pamGrayscaleAlpha:
		depth = 2
	case pamGrayscale:
		depth = 1
	case pamBlackAndWhiteAlpha:
		depth = 2
	case pamBlackAndWhite:
		depth = 1
	default:
		panic(fmt.Sprintf("Internal error processing tuple type %q", opts.TupleType))
	}
	fmt.Fprintln(w, "P7")
	for _, cmt := range opts.Comments {
		cmt = strings.Replace(cmt, "\n", " ", -1)
		cmt = strings.Replace(cmt, "\r", " ", -1)
		fmt.Fprintf(w, "# %s\n", cmt)
	}
	rect := img.Bounds()
	width := rect.Max.X - rect.Min.X
	height := rect.Max.Y - rect.Min.Y
	fmt.Fprintf(w, "WIDTH %d\n", width)
	fmt.Fprintf(w, "HEIGHT %d\n", height)
	fmt.Fprintf(w, "DEPTH %d\n", depth)
	fmt.Fprintf(w, "MAXVAL %d\n", opts.MaxValue)
	fmt.Fprintf(w, "TUPLTYPE %s\n", opts.TupleType)
	fmt.Fprintf(w, "ENDHDR\n")
	if opts.MaxValue < 256 {
		switch ttype {
		case pamColorAlpha:
			return encodeRGBAData(w, img, opts)
		case pamColor:
			return encodeRGBData(w, img, opts)
		case pamGrayscaleAlpha:
			panic("Grayscale + alpha is not currently supported")
		case pamGrayscale:
			return encodeGrayData(w, img, opts)
		case pamBlackAndWhiteAlpha:
			panic("Black & white + alpha is not currently supported")
		case pamBlackAndWhite:
			return encodeBWData(w, img, opts)
		default:
			panic(fmt.Sprintf("Internal error processing tuple type %q", opts.TupleType))
		}
	} else {
		switch ttype {
		case pamColorAlpha:
			return encodeRGBA64Data(w, img, opts)
		case pamColor:
			return encodeRGB64Data(w, img, opts)
		case pamGrayscaleAlpha:
			panic("16-bit grayscale + alpha is not currently supported")
		case pamGrayscale:
			return encodeGray32Data(w, img, opts)
		case pamBlackAndWhiteAlpha:
			panic("16-bit Black & white + alpha is not currently supported")
		case pamBlackAndWhite:
			return encodeBWData(w, img, opts)
		default:
			panic(fmt.Sprintf("Internal error processing tuple type %q", opts.TupleType))
		}
	}
}
func encodeRGBAData(w io.Writer, img image.Image, opts *EncodeOptions) error {
	rect := img.Bounds()
	width := rect.Max.X - rect.Min.X
	samples := make(chan uint16, width*3)
	go func() {
		cm := npcolor.RGBAMModel{M: uint8(opts.MaxValue)}
		for y := rect.Min.Y; y < rect.Max.Y; y++ {
			for x := rect.Min.X; x < rect.Max.X; x++ {
				c := cm.Convert(img.At(x, y)).(npcolor.RGBAM)
				samples <- uint16(c.R)
				samples <- uint16(c.G)
				samples <- uint16(c.B)
				samples <- uint16(c.A)
			}
		}
		close(samples)
	}()
	if opts.Plain {
		return writePlainData(w, samples)
	}
	return writeRawData(w, samples, 1)
}
func encodeRGBA64Data(w io.Writer, img image.Image, opts *EncodeOptions) error {
	rect := img.Bounds()
	width := rect.Max.X - rect.Min.X
	samples := make(chan uint16, width*3)
	go func() {
		cm := npcolor.RGBAM64Model{M: opts.MaxValue}
		for y := rect.Min.Y; y < rect.Max.Y; y++ {
			for x := rect.Min.X; x < rect.Max.X; x++ {
				c := cm.Convert(img.At(x, y)).(npcolor.RGBAM64)
				samples <- c.R
				samples <- c.G
				samples <- c.B
				samples <- c.A
			}
		}
		close(samples)
	}()
	if opts.Plain {
		return writePlainData(w, samples)
	}
	return writeRawData(w, samples, 2)
}
type dummyColor struct{}
func (c dummyColor) RGBA() (uint32, uint32, uint32, uint32) {
	return 0x4000, 0x5000, 0x6000, 0x7000
}
type GrayAM struct {
	Pix []uint8
	Stride int
	Rect image.Rectangle
	Model npcolor.GrayAMModel
}
func (p *GrayAM) ColorModel() color.Model { return p.Model }
func (p *GrayAM) Bounds() image.Rectangle { return p.Rect }
func (p *GrayAM) At(x, y int) color.Color {
	return p.GrayAMAt(x, y)
}
func (p *GrayAM) GrayAMAt(x, y int) npcolor.GrayAM {
	if !(image.Point{x, y}.In(p.Rect)) {
		return npcolor.GrayAM{}
	}
	i := p.PixOffset(x, y)
	return npcolor.GrayAM{
		Y: p.Pix[i+0],
		A: p.Pix[i+1],
		M: p.Model.M,
	}
}
func (p *GrayAM) PixOffset(x, y int) int {
	return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2
}
func (p *GrayAM) Set(x, y int, c color.Color) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	c1 := p.Model.Convert(c).(npcolor.GrayAM)
	p.Pix[i+0] = c1.Y
	p.Pix[i+1] = c1.A
}