// Copyright 2009 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 png

import (
	
	
	
	
	
	
	
	
)

// Encoder configures encoding PNG images.
type Encoder struct {
	CompressionLevel CompressionLevel

	// BufferPool optionally specifies a buffer pool to get temporary
	// EncoderBuffers when encoding an image.
	BufferPool EncoderBufferPool
}

// EncoderBufferPool is an interface for getting and returning temporary
// instances of the [EncoderBuffer] struct. This can be used to reuse buffers
// when encoding multiple images.
type EncoderBufferPool interface {
	Get() *EncoderBuffer
	Put(*EncoderBuffer)
}

// EncoderBuffer holds the buffers used for encoding PNG images.
type EncoderBuffer encoder

type encoder struct {
	enc     *Encoder
	w       io.Writer
	m       image.Image
	cb      int
	err     error
	header  [8]byte
	footer  [4]byte
	tmp     [4 * 256]byte
	cr      [nFilter][]uint8
	pr      []uint8
	zw      *zlib.Writer
	zwLevel int
	bw      *bufio.Writer
}

// CompressionLevel indicates the compression level.
type CompressionLevel int

const (
	DefaultCompression CompressionLevel = 0
	NoCompression      CompressionLevel = -1
	BestSpeed          CompressionLevel = -2
	BestCompression    CompressionLevel = -3

	// Positive CompressionLevel values are reserved to mean a numeric zlib
	// compression level, although that is not implemented yet.
)

type opaquer interface {
	Opaque() bool
}

// Returns whether or not the image is fully opaque.
func opaque( image.Image) bool {
	if ,  := .(opaquer);  {
		return .Opaque()
	}
	 := .Bounds()
	for  := .Min.Y;  < .Max.Y; ++ {
		for  := .Min.X;  < .Max.X; ++ {
			, , ,  := .At(, ).RGBA()
			if  != 0xffff {
				return false
			}
		}
	}
	return true
}

// The absolute value of a byte interpreted as a signed int8.
func abs8( uint8) int {
	if  < 128 {
		return int()
	}
	return 256 - int()
}

func ( *encoder) ( []byte,  string) {
	if .err != nil {
		return
	}
	 := uint32(len())
	if int() != len() {
		.err = UnsupportedError( + " chunk is too large: " + strconv.Itoa(len()))
		return
	}
	binary.BigEndian.PutUint32(.header[:4], )
	.header[4] = [0]
	.header[5] = [1]
	.header[6] = [2]
	.header[7] = [3]
	 := crc32.NewIEEE()
	.Write(.header[4:8])
	.Write()
	binary.BigEndian.PutUint32(.footer[:4], .Sum32())

	_, .err = .w.Write(.header[:8])
	if .err != nil {
		return
	}
	_, .err = .w.Write()
	if .err != nil {
		return
	}
	_, .err = .w.Write(.footer[:4])
}

func ( *encoder) () {
	 := .m.Bounds()
	binary.BigEndian.PutUint32(.tmp[0:4], uint32(.Dx()))
	binary.BigEndian.PutUint32(.tmp[4:8], uint32(.Dy()))
	// Set bit depth and color type.
	switch .cb {
	case cbG8:
		.tmp[8] = 8
		.tmp[9] = ctGrayscale
	case cbTC8:
		.tmp[8] = 8
		.tmp[9] = ctTrueColor
	case cbP8:
		.tmp[8] = 8
		.tmp[9] = ctPaletted
	case cbP4:
		.tmp[8] = 4
		.tmp[9] = ctPaletted
	case cbP2:
		.tmp[8] = 2
		.tmp[9] = ctPaletted
	case cbP1:
		.tmp[8] = 1
		.tmp[9] = ctPaletted
	case cbTCA8:
		.tmp[8] = 8
		.tmp[9] = ctTrueColorAlpha
	case cbG16:
		.tmp[8] = 16
		.tmp[9] = ctGrayscale
	case cbTC16:
		.tmp[8] = 16
		.tmp[9] = ctTrueColor
	case cbTCA16:
		.tmp[8] = 16
		.tmp[9] = ctTrueColorAlpha
	}
	.tmp[10] = 0 // default compression method
	.tmp[11] = 0 // default filter method
	.tmp[12] = 0 // non-interlaced
	.writeChunk(.tmp[:13], "IHDR")
}

func ( *encoder) ( color.Palette) {
	if len() < 1 || len() > 256 {
		.err = FormatError("bad palette length: " + strconv.Itoa(len()))
		return
	}
	 := -1
	for ,  := range  {
		 := color.NRGBAModel.Convert().(color.NRGBA)
		.tmp[3*+0] = .R
		.tmp[3*+1] = .G
		.tmp[3*+2] = .B
		if .A != 0xff {
			 = 
		}
		.tmp[3*256+] = .A
	}
	.writeChunk(.tmp[:3*len()], "PLTE")
	if  != -1 {
		.writeChunk(.tmp[3*256:3*256+1+], "tRNS")
	}
}

// An encoder is an io.Writer that satisfies writes by writing PNG IDAT chunks,
// including an 8-byte header and 4-byte CRC checksum per Write call. Such calls
// should be relatively infrequent, since writeIDATs uses a [bufio.Writer].
//
// This method should only be called from writeIDATs (via writeImage).
// No other code should treat an encoder as an io.Writer.
func ( *encoder) ( []byte) (int, error) {
	.writeChunk(, "IDAT")
	if .err != nil {
		return 0, .err
	}
	return len(), nil
}

// Chooses the filter to use for encoding the current row, and applies it.
// The return value is the index of the filter and also of the row in cr that has had it applied.
func filter( *[nFilter][]byte,  []byte,  int) int {
	// We try all five filter types, and pick the one that minimizes the sum of absolute differences.
	// This is the same heuristic that libpng uses, although the filters are attempted in order of
	// estimated most likely to be minimal (ftUp, ftPaeth, ftNone, ftSub, ftAverage), rather than
	// in their enumeration order (ftNone, ftSub, ftUp, ftAverage, ftPaeth).
	 := [0][1:]
	 := [1][1:]
	 := [2][1:]
	 := [3][1:]
	 := [4][1:]
	 := [1:]
	 := len()

	// The up filter.
	 := 0
	for  := 0;  < ; ++ {
		[] = [] - []
		 += abs8([])
	}
	 := 
	 := ftUp

	// The Paeth filter.
	 = 0
	for  := 0;  < ; ++ {
		[] = [] - []
		 += abs8([])
	}
	for  := ;  < ; ++ {
		[] = [] - paeth([-], [], [-])
		 += abs8([])
		if  >=  {
			break
		}
	}
	if  <  {
		 = 
		 = ftPaeth
	}

	// The none filter.
	 = 0
	for  := 0;  < ; ++ {
		 += abs8([])
		if  >=  {
			break
		}
	}
	if  <  {
		 = 
		 = ftNone
	}

	// The sub filter.
	 = 0
	for  := 0;  < ; ++ {
		[] = []
		 += abs8([])
	}
	for  := ;  < ; ++ {
		[] = [] - [-]
		 += abs8([])
		if  >=  {
			break
		}
	}
	if  <  {
		 = 
		 = ftSub
	}

	// The average filter.
	 = 0
	for  := 0;  < ; ++ {
		[] = [] - []/2
		 += abs8([])
	}
	for  := ;  < ; ++ {
		[] = [] - uint8((int([-])+int([]))/2)
		 += abs8([])
		if  >=  {
			break
		}
	}
	if  <  {
		 = ftAverage
	}

	return 
}

func zeroMemory( []uint8) {
	for  := range  {
		[] = 0
	}
}

func ( *encoder) ( io.Writer,  image.Image,  int,  int) error {
	if .zw == nil || .zwLevel !=  {
		,  := zlib.NewWriterLevel(, )
		if  != nil {
			return 
		}
		.zw = 
		.zwLevel = 
	} else {
		.zw.Reset()
	}
	defer .zw.Close()

	 := 0

	switch  {
	case cbG8:
		 = 8
	case cbTC8:
		 = 24
	case cbP8:
		 = 8
	case cbP4:
		 = 4
	case cbP2:
		 = 2
	case cbP1:
		 = 1
	case cbTCA8:
		 = 32
	case cbTC16:
		 = 48
	case cbTCA16:
		 = 64
	case cbG16:
		 = 16
	}

	// cr[*] and pr are the bytes for the current and previous row.
	// cr[0] is unfiltered (or equivalently, filtered with the ftNone filter).
	// cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the
	// other PNG filter types. These buffers are allocated once and re-used for each row.
	// The +1 is for the per-row filter type, which is at cr[*][0].
	 := .Bounds()
	 := 1 + (*.Dx()+7)/8
	for  := range .cr {
		if cap(.cr[]) <  {
			.cr[] = make([]uint8, )
		} else {
			.cr[] = .cr[][:]
		}
		.cr[][0] = uint8()
	}
	 := .cr
	if cap(.pr) <  {
		.pr = make([]uint8, )
	} else {
		.pr = .pr[:]
		zeroMemory(.pr)
	}
	 := .pr

	,  := .(*image.Gray)
	,  := .(*image.RGBA)
	,  := .(*image.Paletted)
	,  := .(*image.NRGBA)

	for  := .Min.Y;  < .Max.Y; ++ {
		// Convert from colors to bytes.
		 := 1
		switch  {
		case cbG8:
			if  != nil {
				 := ( - .Min.Y) * .Stride
				copy([0][1:], .Pix[:+.Dx()])
			} else {
				for  := .Min.X;  < .Max.X; ++ {
					 := color.GrayModel.Convert(.At(, )).(color.Gray)
					[0][] = .Y
					++
				}
			}
		case cbTC8:
			// We have previously verified that the alpha value is fully opaque.
			 := [0]
			,  := 0, []byte(nil)
			if  != nil {
				,  = .Stride, .Pix
			} else if  != nil {
				,  = .Stride, .Pix
			}
			if  != 0 {
				 := ( - .Min.Y) * 
				 :=  + .Dx()*4
				for  := ;  < ;  += 4 {
					[+0] = [+0]
					[+1] = [+1]
					[+2] = [+2]
					 += 3
				}
			} else {
				for  := .Min.X;  < .Max.X; ++ {
					, , ,  := .At(, ).RGBA()
					[+0] = uint8( >> 8)
					[+1] = uint8( >> 8)
					[+2] = uint8( >> 8)
					 += 3
				}
			}
		case cbP8:
			if  != nil {
				 := ( - .Min.Y) * .Stride
				copy([0][1:], .Pix[:+.Dx()])
			} else {
				 := .(image.PalettedImage)
				for  := .Min.X;  < .Max.X; ++ {
					[0][] = .ColorIndexAt(, )
					 += 1
				}
			}

		case cbP4, cbP2, cbP1:
			 := .(image.PalettedImage)

			var  uint8
			var  int
			 := 8 / 
			for  := .Min.X;  < .Max.X; ++ {
				 = <<uint() | .ColorIndexAt(, )
				++
				if  ==  {
					[0][] = 
					 += 1
					 = 0
					 = 0
				}
			}
			if  != 0 {
				for  !=  {
					 =  << uint()
					++
				}
				[0][] = 
			}

		case cbTCA8:
			if  != nil {
				 := ( - .Min.Y) * .Stride
				copy([0][1:], .Pix[:+.Dx()*4])
			} else if  != nil {
				 := [0][1:]
				 := .Pix[.PixOffset(.Min.X, ):.PixOffset(.Max.X, )]
				for ; len() >= 4; ,  = [4:], [4:] {
					 := (*[4]byte)()
					 := (*[4]byte)()
					if [3] == 0x00 {
						[0] = 0
						[1] = 0
						[2] = 0
						[3] = 0
					} else if [3] == 0xff {
						copy([:], [:])
					} else {
						// This code does the same as color.NRGBAModel.Convert(
						// rgba.At(x, y)).(color.NRGBA) but with no extra memory
						// allocations or interface/function call overhead.
						//
						// The multiplier m combines 0x101 (which converts
						// 8-bit color to 16-bit color) and 0xffff (which, when
						// combined with the division-by-a, converts from
						// alpha-premultiplied to non-alpha-premultiplied).
						const  = 0x101 * 0xffff
						 := uint32([3]) * 0x101
						[0] = uint8((uint32([0]) *  / ) >> 8)
						[1] = uint8((uint32([1]) *  / ) >> 8)
						[2] = uint8((uint32([2]) *  / ) >> 8)
						[3] = [3]
					}
				}
			} else {
				// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
				for  := .Min.X;  < .Max.X; ++ {
					 := color.NRGBAModel.Convert(.At(, )).(color.NRGBA)
					[0][+0] = .R
					[0][+1] = .G
					[0][+2] = .B
					[0][+3] = .A
					 += 4
				}
			}
		case cbG16:
			for  := .Min.X;  < .Max.X; ++ {
				 := color.Gray16Model.Convert(.At(, )).(color.Gray16)
				[0][+0] = uint8(.Y >> 8)
				[0][+1] = uint8(.Y)
				 += 2
			}
		case cbTC16:
			// We have previously verified that the alpha value is fully opaque.
			for  := .Min.X;  < .Max.X; ++ {
				, , ,  := .At(, ).RGBA()
				[0][+0] = uint8( >> 8)
				[0][+1] = uint8()
				[0][+2] = uint8( >> 8)
				[0][+3] = uint8()
				[0][+4] = uint8( >> 8)
				[0][+5] = uint8()
				 += 6
			}
		case cbTCA16:
			// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
			for  := .Min.X;  < .Max.X; ++ {
				 := color.NRGBA64Model.Convert(.At(, )).(color.NRGBA64)
				[0][+0] = uint8(.R >> 8)
				[0][+1] = uint8(.R)
				[0][+2] = uint8(.G >> 8)
				[0][+3] = uint8(.G)
				[0][+4] = uint8(.B >> 8)
				[0][+5] = uint8(.B)
				[0][+6] = uint8(.A >> 8)
				[0][+7] = uint8(.A)
				 += 8
			}
		}

		// Apply the filter.
		// Skip filter for NoCompression and paletted images (cbP8) as
		// "filters are rarely useful on palette images" and will result
		// in larger files (see http://www.libpng.org/pub/png/book/chapter09.html).
		 := ftNone
		if  != zlib.NoCompression &&  != cbP8 &&  != cbP4 &&  != cbP2 &&  != cbP1 {
			// Since we skip paletted images we don't have to worry about
			// bitsPerPixel not being a multiple of 8
			 :=  / 8
			 = filter(&, , )
		}

		// Write the compressed bytes.
		if ,  := .zw.Write([]);  != nil {
			return 
		}

		// The current row for y is the previous row for y+1.
		, [0] = [0], 
	}
	return nil
}

// Write the actual image data to one or more IDAT chunks.
func ( *encoder) () {
	if .err != nil {
		return
	}
	if .bw == nil {
		.bw = bufio.NewWriterSize(, 1<<15)
	} else {
		.bw.Reset()
	}
	.err = .writeImage(.bw, .m, .cb, levelToZlib(.enc.CompressionLevel))
	if .err != nil {
		return
	}
	.err = .bw.Flush()
}

// This function is required because we want the zero value of
// Encoder.CompressionLevel to map to zlib.DefaultCompression.
func levelToZlib( CompressionLevel) int {
	switch  {
	case DefaultCompression:
		return zlib.DefaultCompression
	case NoCompression:
		return zlib.NoCompression
	case BestSpeed:
		return zlib.BestSpeed
	case BestCompression:
		return zlib.BestCompression
	default:
		return zlib.DefaultCompression
	}
}

func ( *encoder) () { .writeChunk(nil, "IEND") }

// Encode writes the Image m to w in PNG format. Any Image may be
// encoded, but images that are not [image.NRGBA] might be encoded lossily.
func ( io.Writer,  image.Image) error {
	var  Encoder
	return .Encode(, )
}

// Encode writes the Image m to w in PNG format.
func ( *Encoder) ( io.Writer,  image.Image) error {
	// Obviously, negative widths and heights are invalid. Furthermore, the PNG
	// spec section 11.2.2 says that zero is invalid. Excessively large images are
	// also rejected.
	,  := int64(.Bounds().Dx()), int64(.Bounds().Dy())
	if  <= 0 ||  <= 0 ||  >= 1<<32 ||  >= 1<<32 {
		return FormatError("invalid image size: " + strconv.FormatInt(, 10) + "x" + strconv.FormatInt(, 10))
	}

	var  *encoder
	if .BufferPool != nil {
		 := .BufferPool.Get()
		 = (*encoder)()

	}
	if  == nil {
		 = &encoder{}
	}
	if .BufferPool != nil {
		defer .BufferPool.Put((*EncoderBuffer)())
	}

	.enc = 
	.w = 
	.m = 

	var  color.Palette
	// cbP8 encoding needs PalettedImage's ColorIndexAt method.
	if ,  := .(image.PalettedImage);  {
		, _ = .ColorModel().(color.Palette)
	}
	if  != nil {
		if len() <= 2 {
			.cb = cbP1
		} else if len() <= 4 {
			.cb = cbP2
		} else if len() <= 16 {
			.cb = cbP4
		} else {
			.cb = cbP8
		}
	} else {
		switch .ColorModel() {
		case color.GrayModel:
			.cb = cbG8
		case color.Gray16Model:
			.cb = cbG16
		case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
			if opaque() {
				.cb = cbTC8
			} else {
				.cb = cbTCA8
			}
		default:
			if opaque() {
				.cb = cbTC16
			} else {
				.cb = cbTCA16
			}
		}
	}

	_, .err = io.WriteString(, pngHeader)
	.writeIHDR()
	if  != nil {
		.writePLTEAndTRNS()
	}
	.writeIDATs()
	.writeIEND()
	return .err
}