// Copyright 2012 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 tiff

import (
	
	
	
	
	
	
	
)

// The TIFF format allows to choose the order of the different elements freely.
// The basic structure of a TIFF file written by this package is:
//
//   1. Header (8 bytes).
//   2. Image data.
//   3. Image File Directory (IFD).
//   4. "Pointer area" for larger entries in the IFD.

// We only write little-endian TIFF files.
var enc = binary.LittleEndian

// An ifdEntry is a single entry in an Image File Directory.
// A value of type dtRational is composed of two 32-bit values,
// thus data contains two uints (numerator and denominator) for a single number.
type ifdEntry struct {
	tag      int
	datatype int
	data     []uint32
}

func ( ifdEntry) ( []byte) {
	for ,  := range .data {
		switch .datatype {
		case dtByte, dtASCII:
			[0] = byte()
			 = [1:]
		case dtShort:
			enc.PutUint16(, uint16())
			 = [2:]
		case dtLong, dtRational:
			enc.PutUint32(, uint32())
			 = [4:]
		}
	}
}

type byTag []ifdEntry

func ( byTag) () int           { return len() }
func ( byTag) (,  int) bool { return [].tag < [].tag }
func ( byTag) (,  int)      { [], [] = [], [] }

func encodeGray( io.Writer,  []uint8, , ,  int,  bool) error {
	if ! {
		return writePix(, , , , )
	}
	 := make([]byte, )
	for  := 0;  < ; ++ {
		 := * + 0
		 := * + 
		 := 0
		var  uint8
		for  := ;  < ; ++ {
			 := []
			[] =  - 
			 = 
			++
		}
		if ,  := .Write();  != nil {
			return 
		}
	}
	return nil
}

func encodeGray16( io.Writer,  []uint8, , ,  int,  bool) error {
	 := make([]byte, *2)
	for  := 0;  < ; ++ {
		 := * + 0
		 := * + *2
		 := 0
		var  uint16
		for  := ;  < ;  += 2 {
			// An image.Gray16's Pix is in big-endian order.
			 := uint16([])<<8 | uint16([+1])
			if  {
				,  = , -
			}
			// We only write little-endian TIFF files.
			[+0] = byte()
			[+1] = byte( >> 8)
			 += 2
		}
		if ,  := .Write();  != nil {
			return 
		}
	}
	return nil
}

func encodeRGBA( io.Writer,  []uint8, , ,  int,  bool) error {
	if ! {
		return writePix(, , , *4, )
	}
	 := make([]byte, *4)
	for  := 0;  < ; ++ {
		 := * + 0
		 := * + *4
		 := 0
		var , , ,  uint8
		for  := ;  < ;  += 4 {
			, , ,  := [+0], [+1], [+2], [+3]
			[+0] =  - 
			[+1] =  - 
			[+2] =  - 
			[+3] =  - 
			 += 4
			, , ,  = , , , 
		}
		if ,  := .Write();  != nil {
			return 
		}
	}
	return nil
}

func encodeRGBA64( io.Writer,  []uint8, , ,  int,  bool) error {
	 := make([]byte, *8)
	for  := 0;  < ; ++ {
		 := * + 0
		 := * + *8
		 := 0
		var , , ,  uint16
		for  := ;  < ;  += 8 {
			// An image.RGBA64's Pix is in big-endian order.
			 := uint16([+0])<<8 | uint16([+1])
			 := uint16([+2])<<8 | uint16([+3])
			 := uint16([+4])<<8 | uint16([+5])
			 := uint16([+6])<<8 | uint16([+7])
			if  {
				,  = , -
				,  = , -
				,  = , -
				,  = , -
			}
			// We only write little-endian TIFF files.
			[+0] = byte()
			[+1] = byte( >> 8)
			[+2] = byte()
			[+3] = byte( >> 8)
			[+4] = byte()
			[+5] = byte( >> 8)
			[+6] = byte()
			[+7] = byte( >> 8)
			 += 8
		}
		if ,  := .Write();  != nil {
			return 
		}
	}
	return nil
}

func encode( io.Writer,  image.Image,  bool) error {
	 := .Bounds()
	 := make([]byte, 4*.Dx())
	for  := .Min.Y;  < .Max.Y; ++ {
		 := 0
		if  {
			var , , ,  uint8
			for  := .Min.X;  < .Max.X; ++ {
				, , ,  := .At(, ).RGBA()
				 := uint8( >> 8)
				 := uint8( >> 8)
				 := uint8( >> 8)
				 := uint8( >> 8)
				[+0] =  - 
				[+1] =  - 
				[+2] =  - 
				[+3] =  - 
				 += 4
				, , ,  = , , , 
			}
		} else {
			for  := .Min.X;  < .Max.X; ++ {
				, , ,  := .At(, ).RGBA()
				[+0] = uint8( >> 8)
				[+1] = uint8( >> 8)
				[+2] = uint8( >> 8)
				[+3] = uint8( >> 8)
				 += 4
			}
		}
		if ,  := .Write();  != nil {
			return 
		}
	}
	return nil
}

// writePix writes the internal byte array of an image to w. It is less general
// but much faster then encode. writePix is used when pix directly
// corresponds to one of the TIFF image types.
func writePix( io.Writer,  []byte, , ,  int) error {
	if  ==  {
		,  := .Write([:*])
		return 
	}
	for ;  > 0; -- {
		if ,  := .Write([:]);  != nil {
			return 
		}
		 = [:]
	}
	return nil
}

func writeIFD( io.Writer,  int,  []ifdEntry) error {
	var  [ifdLen]byte
	// Make space for "pointer area" containing IFD entry data
	// longer than 4 bytes.
	 := make([]byte, 1024)
	 :=  + ifdLen*len() + 6
	var  int // Current offset in parea.

	// The IFD has to be written with the tags in ascending order.
	sort.Sort(byTag())

	// Write the number of entries in this IFD.
	if  := binary.Write(, enc, uint16(len()));  != nil {
		return 
	}
	for ,  := range  {
		enc.PutUint16([0:2], uint16(.tag))
		enc.PutUint16([2:4], uint16(.datatype))
		 := uint32(len(.data))
		if .datatype == dtRational {
			 /= 2
		}
		enc.PutUint32([4:8], )
		 := int( * lengths[.datatype])
		if  <= 4 {
			.putData([8:12])
		} else {
			if ( + ) > len() {
				 := len() + 1024
				for ( + ) >  {
					 += 1024
				}
				 := make([]byte, )
				copy(, )
				 = 
			}
			.putData([ : +])
			enc.PutUint32([8:12], uint32(+))
			 += 
		}
		if ,  := .Write([:]);  != nil {
			return 
		}
	}
	// The IFD ends with the offset of the next IFD in the file,
	// or zero if it is the last one (page 14).
	if  := binary.Write(, enc, uint32(0));  != nil {
		return 
	}
	,  := .Write([:])
	return 
}

// Options are the encoding parameters.
type Options struct {
	// Compression is the type of compression used.
	Compression CompressionType
	// Predictor determines whether a differencing predictor is used;
	// if true, instead of each pixel's color, the color difference to the
	// preceding one is saved. This improves the compression for certain
	// types of images and compressors. For example, it works well for
	// photos with Deflate compression.
	Predictor bool
}

// Encode writes the image m to w. opt determines the options used for
// encoding, such as the compression type. If opt is nil, an uncompressed
// image is written.
func ( io.Writer,  image.Image,  *Options) error {
	 := .Bounds().Size()

	if .X == 0 || .Y == 0 {
		return errors.New("tiff: zero-size image")
	}

	 := uint32(cNone)
	 := false
	if  != nil {
		 = .Compression.specValue()
		// The predictor field is only used with LZW. See page 64 of the spec.
		 = .Predictor &&  == cLZW
	}

	,  := io.WriteString(, leHeader)
	if  != nil {
		return 
	}

	// Compressed data is written into a buffer first, so that we
	// know the compressed size.
	var  bytes.Buffer
	// dst holds the destination for the pixel data of the image --
	// either w or a writer to buf.
	var  io.Writer
	// imageLen is the length of the pixel data in bytes.
	// The offset of the IFD is imageLen + 8 header bytes.
	var  int

	switch  {
	case cNone:
		 = 
		// Write IFD offset before outputting pixel data.
		switch .(type) {
		case *image.Paletted:
			 = .X * .Y * 1
		case *image.Gray:
			 = .X * .Y * 1
		case *image.Gray16:
			 = .X * .Y * 2
		case *image.RGBA64:
			 = .X * .Y * 8
		case *image.NRGBA64:
			 = .X * .Y * 8
		default:
			 = .X * .Y * 4
		}
		 = binary.Write(, enc, uint32(+8))
		if  != nil {
			return 
		}
	case cDeflate:
		 = zlib.NewWriter(&)
	default:
		return errors.New("tiff: unsupported compression")
	}

	 := uint32(prNone)
	 := uint32(pRGB)
	 := uint32(4)
	 := []uint32{8, 8, 8, 8}
	 := uint32(0)
	 := []uint32{}

	if  {
		 = prHorizontal
	}
	switch m := .(type) {
	case *image.Paletted:
		 = pPaletted
		 = 1
		 = []uint32{8}
		 = make([]uint32, 256*3)
		for  := 0;  < 256 &&  < len(.Palette); ++ {
			, , ,  := .Palette[].RGBA()
			[+0*256] = uint32()
			[+1*256] = uint32()
			[+2*256] = uint32()
		}
		 = encodeGray(, .Pix, .X, .Y, .Stride, )
	case *image.Gray:
		 = pBlackIsZero
		 = 1
		 = []uint32{8}
		 = encodeGray(, .Pix, .X, .Y, .Stride, )
	case *image.Gray16:
		 = pBlackIsZero
		 = 1
		 = []uint32{16}
		 = encodeGray16(, .Pix, .X, .Y, .Stride, )
	case *image.NRGBA:
		 = 2 // Unassociated alpha.
		 = encodeRGBA(, .Pix, .X, .Y, .Stride, )
	case *image.NRGBA64:
		 = 2 // Unassociated alpha.
		 = []uint32{16, 16, 16, 16}
		 = encodeRGBA64(, .Pix, .X, .Y, .Stride, )
	case *image.RGBA:
		 = 1 // Associated alpha.
		 = encodeRGBA(, .Pix, .X, .Y, .Stride, )
	case *image.RGBA64:
		 = 1 // Associated alpha.
		 = []uint32{16, 16, 16, 16}
		 = encodeRGBA64(, .Pix, .X, .Y, .Stride, )
	default:
		 = 1 // Associated alpha.
		 = encode(, , )
	}
	if  != nil {
		return 
	}

	if  != cNone {
		if  = .(io.Closer).Close();  != nil {
			return 
		}
		 = .Len()
		if  = binary.Write(, enc, uint32(+8));  != nil {
			return 
		}
		if _,  = .WriteTo();  != nil {
			return 
		}
	}

	 := []ifdEntry{
		{tImageWidth, dtShort, []uint32{uint32(.X)}},
		{tImageLength, dtShort, []uint32{uint32(.Y)}},
		{tBitsPerSample, dtShort, },
		{tCompression, dtShort, []uint32{}},
		{tPhotometricInterpretation, dtShort, []uint32{}},
		{tStripOffsets, dtLong, []uint32{8}},
		{tSamplesPerPixel, dtShort, []uint32{}},
		{tRowsPerStrip, dtShort, []uint32{uint32(.Y)}},
		{tStripByteCounts, dtLong, []uint32{uint32()}},
		// There is currently no support for storing the image
		// resolution, so give a bogus value of 72x72 dpi.
		{tXResolution, dtRational, []uint32{72, 1}},
		{tYResolution, dtRational, []uint32{72, 1}},
		{tResolutionUnit, dtShort, []uint32{resPerInch}},
	}
	if  != prNone {
		 = append(, ifdEntry{tPredictor, dtShort, []uint32{}})
	}
	if len() != 0 {
		 = append(, ifdEntry{tColorMap, dtShort, })
	}
	if  > 0 {
		 = append(, ifdEntry{tExtraSamples, dtShort, []uint32{}})
	}

	return writeIFD(, +8, )
}