package vnc2video

import (
	"bytes"
	"compress/zlib"
	"encoding/binary"
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/jpeg"
	"io"
	"math"
	"vnc2video/logger"
)

//go:generate stringer -type=TightCompression

type TightCompression uint8

const (
	TightCompressionBasic = 0
	TightCompressionFill  = 8
	TightCompressionJPEG  = 9
	TightCompressionPNG   = 10
)

//go:generate stringer -type=TightFilter

type TightFilter uint8

const (
	TightFilterCopy     = 0
	TightFilterPalette  = 1
	TightFilterGradient = 2
)

type TightEncoding struct {
	Image        image.Image
	decoders     []io.Reader
	decoderBuffs []*bytes.Buffer
}

var instance *TightEncoding
var TightMinToCompress int = 12

func (*TightEncoding) Supported(Conn) bool {
	return true
}

func (*TightEncoding) Type() EncodingType { return EncTight }

func (*TightEncoding) GetInstance() *TightEncoding {
	if instance == nil {
		instance = &TightEncoding{}
	}
	return instance
}

func (enc *TightEncoding) Write(c Conn, rect *Rectangle) error {
	return nil
}

// Read unmarshal color from conn
func getTightColor(c io.Reader, pf *PixelFormat) (*color.RGBA64, error) {
	if pf.TrueColor == 0 {
		return nil, errors.New("support for non true color formats was not implemented")
	}
	order := pf.order()
	var pixel uint32
	isTightFormat := pf.TrueColor != 0 && pf.Depth == 24 && pf.BPP == 32 && pf.BlueMax <= 255 && pf.RedMax <= 255 && pf.GreenMax <= 255
	if isTightFormat {
		//tbytes := make([]byte, 3)
		tbytes, err := ReadBytes(3, c)
		if err != nil {
			return nil, err
		}
		rgb := color.RGBA64{
			R: uint16(tbytes[0]),
			G: uint16(tbytes[1]),
			B: uint16(tbytes[2]),
			A: uint16(1),
		}
		return &rgb, nil
	}

	switch pf.BPP {
	case 8:
		var px uint8
		if err := binary.Read(c, order, &px); err != nil {
			return nil, err
		}
		pixel = uint32(px)
	case 16:
		var px uint16
		if err := binary.Read(c, order, &px); err != nil {
			return nil, err
		}
		pixel = uint32(px)
	case 32:
		var px uint32
		if err := binary.Read(c, order, &px); err != nil {
			return nil, err
		}
		pixel = uint32(px)
	}

	rgb := color.RGBA64{
		R: uint16((pixel >> pf.RedShift) & uint32(pf.RedMax)),
		G: uint16((pixel >> pf.GreenShift) & uint32(pf.GreenMax)),
		B: uint16((pixel >> pf.BlueShift) & uint32(pf.BlueMax)),
	}

	return &rgb, nil
}

func calcTightBytePerPixel(pf *PixelFormat) int {
	bytesPerPixel := int(pf.BPP / 8)

	var bytesPerPixelTight int
	if 24 == pf.Depth && 32 == pf.BPP {
		bytesPerPixelTight = 3
	} else {
		bytesPerPixelTight = bytesPerPixel
	}
	return bytesPerPixelTight
}

func (enc *TightEncoding) Reset() error {
	//enc.decoders = make([]io.Reader, 4)
	//enc.decoderBuffs = make([]*bytes.Buffer, 4)
	return nil
}

func (enc *TightEncoding) resetDecoders(compControl uint8) {
	logger.Debugf("###resetDecoders compctl :%d", 0x0F&compControl)
	for i := 0; i < 4; i++ {
		if (compControl&1) != 0 && enc.decoders[i] != nil {
			logger.Debugf("###resetDecoders - resetting decoder #%d", i)
			enc.decoders[i] = nil //.(zlib.Resetter).Reset(nil,nil);
		}
		compControl >>= 1
	}
}

func (enc *TightEncoding) SetTargetImage(img draw.Image) {
	enc.Image = img
}

var counter int = 0

func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error {

	var err error
	////////////
	// if counter > 40 {
	// 	os.Exit(1)
	// }
	////////////
	pixelFmt := c.PixelFormat()
	bytesPixel := calcTightBytePerPixel(&pixelFmt)
	if enc.Image == nil {
		enc.Image = image.NewRGBA(image.Rect(0, 0, int(c.Width()), int(c.Height())))
	}

	//r.StartByteCollection()

	//r.StartByteCollection()
	// defer func() {
	// 	t.bytes = r.EndByteCollection()
	// }()

	compctl, err := ReadUint8(c)

	/////////////////
	// var out *os.File
	// if out == nil {
	// 	out, err = os.Create("./output" + strconv.Itoa(counter) + "-" + strconv.Itoa(int(compctl)) + ".jpg")
	// 	if err != nil {
	// 		fmt.Println(err)
	// 		os.Exit(1)
	// 	}
	// }
	// defer func() { counter++ }()
	// defer jpeg.Encode(out, enc.Image, nil)
	//////////////
	logger.Debugf("-----------READ-Tight-encoding compctl=%d -------------", compctl)

	if err != nil {
		logger.Errorf("error in handling tight encoding: %v", err)
		return err
	}
	//logger.Debugf("bytesPixel= %d, subencoding= %d", bytesPixel, compctl)
	enc.resetDecoders(compctl)

	//move it to position (remove zlib flush commands)
	compType := compctl >> 4 & 0x0F

	//logger.Debugf("afterSHL:%d", compType)
	switch compType {
	case TightCompressionFill:
		logger.Debugf("--TIGHT_FILL: reading fill size=%d,counter=%d", bytesPixel, counter)
		//read color
		pf := c.PixelFormat()
		rectColor, err := getTightColor(c, &pf)
		if err != nil {
			logger.Errorf("error in reading tight encoding: %v", err)
			return err
		}

		c1 := color.RGBAModel.Convert(rectColor).(color.RGBA)
		dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA)
		var x, y int

		for y = int(rect.Y); y < int(rect.Height+rect.Y); y++ {
			for x = int(rect.X); x < int(rect.Width+rect.X); x++ {
				offset := dst.PixOffset(x, y)
				dst.Pix[offset+0] = c1.R
				dst.Pix[offset+1] = c1.G
				dst.Pix[offset+2] = c1.B
				dst.Pix[offset+3] = c1.A
			}
		}

		if bytesPixel != 3 {
			return fmt.Errorf("non tight bytesPerPixel format, should be 3 bytes")
		}
		return nil
	case TightCompressionJPEG:
		logger.Debugf("--TIGHT_JPEG,counter=%d", counter)
		if pixelFmt.BPP == 8 {
			return errors.New("Tight encoding: JPEG is not supported in 8 bpp mode")
		}

		len, err := readTightLength(c)

		if err != nil {
			return err
		}
		//logger.Debugf("reading jpeg, size=%d\n", len)
		jpegBytes, err := ReadBytes(len, c)
		if err != nil {
			return err
		}
		//TODO: check if we can read jpeg directly from stream (this is safer for now)
		buff := bytes.NewBuffer(jpegBytes)
		img, err := jpeg.Decode(buff)
		if err != nil {
			logger.Error("problem while decoding jpeg:", err)
		}
		dest := enc.Image.(draw.Image)
		draw.Draw(dest, dest.Bounds(), img, image.Point{int(rect.X), int(rect.Y)}, draw.Src)

		return nil
	default:

		if compType > TightCompressionJPEG {
			logger.Error("Compression control byte is incorrect!")
		}

		enc.handleTightFilters(compctl, &pixelFmt, rect, c)

		return nil
	}
}

func (enc *TightEncoding) handleTightFilters(compCtl uint8, pixelFmt *PixelFormat, rect *Rectangle, r Conn) {
	var STREAM_ID_MASK uint8 = 0x30
	var FILTER_ID_MASK uint8 = 0x40

	var filterid uint8
	var err error

	decoderId := (compCtl & STREAM_ID_MASK) >> 4

	for len(enc.decoders) < 4 {
		enc.decoders = append(enc.decoders, nil)
		enc.decoderBuffs = append(enc.decoderBuffs, nil)
	}

	if (compCtl & FILTER_ID_MASK) > 0 {
		filterid, err = ReadUint8(r)

		if err != nil {
			logger.Errorf("error in handling tight encoding, reading filterid: %v", err)
			return
		}
		//logger.Debugf("handleTightFilters: read filter: %d", filterid)
	}

	bytesPixel := calcTightBytePerPixel(pixelFmt)

	//logger.Debugf("handleTightFilters: filter: %d", filterid)

	lengthCurrentbpp := int(bytesPixel) * int(rect.Width) * int(rect.Height)

	switch filterid {
	case TightFilterPalette: //PALETTE_FILTER

		palette, err := enc.readTightPalette(r, bytesPixel)
		if err != nil {
			logger.Errorf("handleTightFilters: error in Reading Palette: %v", err)
			return
		}
		logger.Debugf("----PALETTE_FILTER,palette len=%d counter=%d, rect= %v", len(palette), counter, rect)

		//logger.Debugf("got palette: %v", palette)
		var dataLength int
		if len(palette) == 2 {
			dataLength = int(rect.Height) * ((int(rect.Width) + 7) / 8)
		} else {
			dataLength = int(rect.Width * rect.Height)
		}
		tightBytes, err := enc.ReadTightData(dataLength, r, int(decoderId))
		//logger.Debugf("got tightBytes: %v", tightBytes)
		if err != nil {
			logger.Errorf("handleTightFilters: error in handling tight encoding, reading palette filter data: %v", err)
			return
		}
		//logger.Errorf("handleTightFilters: got tight data: %v", tightBytes)

		enc.drawTightPalette(rect, palette, tightBytes)
		//enc.Image = myImg
	case TightFilterGradient: //GRADIENT_FILTER
		logger.Debugf("----GRADIENT_FILTER: bytesPixel=%d, counter=%d", bytesPixel, counter)
		//logger.Debugf("usegrad: %d\n", filterid)
		data, err := enc.ReadTightData(lengthCurrentbpp, r, int(decoderId))
		if err != nil {
			logger.Errorf("handleTightFilters: error in handling tight encoding, Reading GRADIENT_FILTER: %v", err)
			return
		}
		enc.decodeGradData(rect, data)

	case TightFilterCopy: //BASIC_FILTER
		//lengthCurrentbpp1 := int(pixelFmt.BPP/8) * int(rect.Width) * int(rect.Height)
		logger.Debugf("----BASIC_FILTER: bytesPixel=%d, counter=%d", bytesPixel, counter)

		tightBytes, err := enc.ReadTightData(lengthCurrentbpp, r, int(decoderId))
		if err != nil {
			logger.Errorf("handleTightFilters: error in handling tight encoding, Reading BASIC_FILTER: %v", err)
			return
		}
		logger.Debugf("tightBytes len= %d", len(tightBytes))
		enc.drawTightBytes(tightBytes, rect)
	default:
		logger.Errorf("handleTightFilters: Bad tight filter id: %d", filterid)
		return
	}

	return
}
func (enc *TightEncoding) drawTightPalette(rect *Rectangle, palette color.Palette, tightBytes []byte) {
	myImg := enc.Image.(draw.Image)
	bytePos := 0
	bitPos := 0
	var palettePos int
	for i := 0; i < int(rect.Height); i++ {
		for j := 0; j < int(rect.Width); j++ {
			if len(palette) == 2 {
				currByte := tightBytes[bytePos]
				palettePos = int(currByte&byte(math.Pow(2.0, float64(bitPos)))) >> uint(bitPos)
				//logger.Debugf("palletPos=%d, bitpos=%d, bytepos=%d", palettePos, bitPos, bytePos)
				bytePos = bytePos + int((bitPos+1.0)/8.0)
				bitPos = (bitPos + 1) % 8
				//logger.Debugf("next: bitpos=%d, bytepos=%d", bitPos, bytePos)
			} else {
				palettePos = int(tightBytes[bytePos])
				bytePos++
			}
			myImg.Set(int(rect.X)+j, int(rect.Y)+i, palette[palettePos])
			//logger.Debugf("(%d,%d): pos: %d col:%d", int(rect.X)+j, int(rect.Y)+i, palettePos, palette[palettePos])
		}
	}
}
func (enc *TightEncoding) decodeGradData(rect *Rectangle, buffer []byte) {

	logger.Debugf("putting gradient size: %v on image: %v", rect, enc.Image.Bounds())

	prevRow := make([]byte, rect.Width*3+3) //new byte[w * 3];
	thisRow := make([]byte, rect.Width*3+3) //new byte[w * 3];

	bIdx := 0
	dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA)

	for i := 0; i < int(rect.Height); i++ {
		for j := 3; j < int(rect.Width*3+3); j += 3 {
			d := int(0xff&prevRow[j]) + // "upper" pixel (from prev row)
				int(0xff&thisRow[j-3]) - // prev pixel
				int(0xff&prevRow[j-3]) // "diagonal" prev pixel
			if d < 0 {
				d = 0
			}
			if d > 255 {
				d = 255
			}
			red := int(buffer[bIdx]) + d
			thisRow[j] = byte(red & 255)

			d = int(0xff&prevRow[j+1]) +
				int(0xff&thisRow[j+1-3]) -
				int(0xff&prevRow[j+1-3])
			if d < 0 {
				d = 0
			}
			if d > 255 {
				d = 255
			}
			green := int(buffer[bIdx+1]) + d
			thisRow[j+1] = byte(green & 255)

			d = int(0xff&prevRow[j+2]) +
				int(0xff&thisRow[j+2-3]) -
				int(0xff&prevRow[j+2-3])
			if d < 0 {
				d = 0
			}
			if d > 255 {
				d = 255
			}
			blue := int(buffer[bIdx+2]) + d
			thisRow[j+2] = byte(blue & 255)

			bIdx += 3
		}

		for idx := 3; idx < (len(thisRow) - 3); idx += 3 {
			myColor := color.RGBA{R: (thisRow[idx]), G: (thisRow[idx+1]), B: (thisRow[idx+2]), A: 1}
			dst.SetRGBA(idx/3+int(rect.X)-1, int(rect.Y)+i, myColor)
			//logger.Debugf("putting pixel: idx=%d, pos=(%d,%d), col=%v", idx, idx/3+int(rect.X), int(rect.Y)+i, myColor)

		}

		// exchange thisRow and prevRow:
		tempRow := thisRow
		thisRow = prevRow
		prevRow = tempRow
	}
}

func (enc *TightEncoding) decodeGradientData(rect *Rectangle, buf []byte) {
	logger.Debugf("putting gradient on image: %v", enc.Image.Bounds())
	var dx, dy, c int
	prevRow := make([]byte, rect.Width*3) //new byte[w * 3];
	thisRow := make([]byte, rect.Width*3) //new byte[w * 3];
	pix := make([]byte, 3)
	est := make([]int, 3)

	dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA)
	//offset := int(rect.Y)*dst.Bounds().Max.X + int(rect.X)

	for dy = 0; dy < int(rect.Height); dy++ {
		//offset := dst.PixOffset(x, y)
		/* First pixel in a row */
		for c = 0; c < 3; c++ {
			pix[c] = byte(prevRow[c] + buf[dy*int(rect.Width)*3+c])
			thisRow[c] = pix[c]
		}
		//logger.Debugf("putting pixel:%d,%d,%d at offset: %d, pixArrayLen= %v, rect=x:%d,y:%d,w:%d,h:%d, Yposition=%d", pix[0], pix[1], pix[2], offset, len(dst.Pix), rect.X, rect.Y, rect.Width, rect.Height, dy)
		myColor := color.RGBA{R: (pix[0]), G: (pix[1]), B: (pix[2]), A: 1}
		dst.SetRGBA(int(rect.X), dy+int(rect.Y), myColor)

		/* Remaining pixels of a row */
		for dx = 1; dx < int(rect.Width); dx++ {
			for c = 0; c < 3; c++ {
				est[c] = int((prevRow[dx*3+c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx-1)*3+c] & 0xFF))
				if est[c] > 0xFF {
					est[c] = 0xFF
				} else if est[c] < 0x00 {
					est[c] = 0x00
				}
				pix[c] = (byte)(byte(est[c]) + buf[(dy*int(rect.Width)+dx)*3+c])
				thisRow[dx*3+c] = pix[c]
			}
			//logger.Debugf("putting pixel:%d,%d,%d at offset: %d, pixArrayLen= %v, rect=x:%d,y:%d,w:%d,h:%d, Yposition=%d", pix[0], pix[1], pix[2], offset, len(dst.Pix), x, y, w, h, dy)
			myColor := color.RGBA{R: pix[0], G: (pix[1]), B: (pix[2]), A: 1}
			dst.SetRGBA(dx+int(rect.X), dy+int(rect.Y), myColor)

		}

		copy(prevRow, thisRow)
	}
	enc.Image = dst
}

func ReadBytes(count int, r io.Reader) ([]byte, error) {
	buff := make([]byte, count)

	lengthRead, err := io.ReadFull(r, buff)

	//lengthRead, err := r.Read(buff)
	if lengthRead != count {
		logger.Errorf("RfbReadHelper.ReadBytes unable to read bytes: lengthRead=%d, countExpected=%d", lengthRead, count)
		return nil, errors.New("RfbReadHelper.ReadBytes unable to read bytes")
	}

	//err := binary.Read(r, binary.BigEndian, &buff)

	if err != nil {
		logger.Errorf("RfbReadHelper.ReadBytes error while reading bytes: ", err)
		//if err := binary.Read(d.conn, binary.BigEndian, &buff); err != nil {
		return nil, err
	}

	return buff, nil
}

func (enc *TightEncoding) readTightPalette(connReader Conn, bytesPixel int) (color.Palette, error) {

	colorCount, err := ReadUint8(connReader)
	if err != nil {
		logger.Errorf("handleTightFilters: error in handling tight encoding, reading TightFilterPalette: %v", err)
		return nil, err
	}

	paletteSize := colorCount + 1 // add one more
	//logger.Debugf("----PALETTE_FILTER: paletteSize=%d bytesPixel=%d\n", paletteSize, bytesPixel)
	//complete palette
	paletteColorBytes, err := ReadBytes(int(paletteSize)*bytesPixel, connReader)
	if err != nil {
		logger.Errorf("handleTightFilters: error in handling tight encoding, reading TightFilterPalette.paletteSize: %v", err)
		return nil, err
	}
	var paletteColors color.Palette = make([]color.Color, 0)
	for i := 0; i < int(paletteSize)*bytesPixel; i += 3 {
		col := color.RGBA{R: paletteColorBytes[i], G: paletteColorBytes[i+1], B: paletteColorBytes[i+2], A: 1}
		paletteColors = append(paletteColors, col)
	}
	return paletteColors, nil
}

func (enc *TightEncoding) ReadTightData(dataSize int, c Conn, decoderId int) ([]byte, error) {

	logger.Debugf(">>> Reading zipped tight data from decoder: %d", decoderId)
	if int(dataSize) < TightMinToCompress {
		return ReadBytes(int(dataSize), c)
	}
	zlibDataLen, err := readTightLength(c)
	//logger.Debugf("RfbReadHelper.ReadTightData: compactlen=%d", zlibDataLen)
	if err != nil {
		return nil, err
	}
	zippedBytes, err := ReadBytes(zlibDataLen, c)
	if err != nil {
		return nil, err
	}
	var r io.Reader
	if enc.decoders[decoderId] == nil {
		b := bytes.NewBuffer(zippedBytes)
		r, err = zlib.NewReader(b)
		enc.decoders[decoderId] = r
		enc.decoderBuffs[decoderId] = b
	} else {
		b := enc.decoderBuffs[decoderId]

		b.Write(zippedBytes) //set the underlaying buffer to new content (not resetting the decoder zlib stream)
		r = enc.decoders[decoderId]
	}

	retBytes := make([]byte, dataSize)
	count, err := io.ReadFull(r, retBytes)
	if err != nil {
		return nil, err
	}
	if count != dataSize {
		return nil, errors.New("ReadTightData: reading inflating zip didn't produce expected number of bytes")
	}
	return retBytes, nil
}

type TightCC struct {
	Compression TightCompression
	Filter      TightFilter
}

func readTightCC(c Conn) (*TightCC, error) {
	var ccb uint8 // compression control byte
	if err := binary.Read(c, binary.BigEndian, &ccb); err != nil {
		return nil, err
	}
	cmp := TightCompression(ccb >> 4)
	switch cmp {
	case TightCompressionBasic:
		return &TightCC{TightCompressionBasic, TightFilterCopy}, nil
	case TightCompressionFill:
		return &TightCC{TightCompressionFill, TightFilterCopy}, nil
	case TightCompressionPNG:
		return &TightCC{TightCompressionPNG, TightFilterCopy}, nil
	}
	return nil, fmt.Errorf("unknown tight compression %d", cmp)
}

func writeTightCC(c Conn, tcc *TightCC) error {
	var ccb uint8 // compression control byte
	switch tcc.Compression {
	case TightCompressionFill:
		ccb = setBit(ccb, 7)
	case TightCompressionJPEG:
		ccb = setBit(ccb, 7)
		ccb = setBit(ccb, 4)
	case TightCompressionPNG:
		ccb = setBit(ccb, 7)
		ccb = setBit(ccb, 5)
	}
	return binary.Write(c, binary.BigEndian, ccb)
}

type TightPixel struct {
	R uint8
	G uint8
	B uint8
}

func writeTightLength(c Conn, l int) error {
	var buf []uint8

	buf = append(buf, uint8(l&0x7F))
	if l > 0x7F {
		buf[0] |= 0x80
		buf = append(buf, uint8((l>>7)&0x7F))
		if l > 0x3FFF {
			buf[1] |= 0x80
			buf = append(buf, uint8((l>>14)&0xFF))
		}
	}
	return binary.Write(c, binary.BigEndian, buf)
}

func readTightLength(c Conn) (int, error) {
	var length int
	var err error
	var b uint8

	if err = binary.Read(c, binary.BigEndian, &b); err != nil {
		return 0, err
	}

	length = int(b) & 0x7F
	if (b & 0x80) == 0 {
		return length, nil
	}

	if err = binary.Read(c, binary.BigEndian, &b); err != nil {
		return 0, err
	}
	length |= (int(b) & 0x7F) << 7
	if (b & 0x80) == 0 {
		return length, nil
	}

	if err = binary.Read(c, binary.BigEndian, &b); err != nil {
		return 0, err
	}
	length |= (int(b) & 0xFF) << 14

	return length, nil
}

/**
 * Draw byte array bitmap data (for Tight)
 */
func (enc *TightEncoding) drawTightBytes(bytes []byte, rect *Rectangle) {
	bytesPos := 0
	myImg := (enc.Image).(draw.Image)
	logger.Debugf("drawTightBytes: len(bytes)= %d, %v", len(bytes), rect)

	for ly := rect.Y; ly < rect.Y+rect.Height; ly++ {
		for lx := rect.X; lx < rect.X+rect.Width; lx++ {
			color := color.RGBA{R: bytes[bytesPos], G: bytes[bytesPos+1], B: bytes[bytesPos+2], A: 1}
			//logger.Debugf("drawTightBytes: setting pixel= (%d,%d): %v", int(lx), int(ly), color)
			myImg.Set(int(lx), int(ly), color)

			bytesPos += 3
		}
	}
	//enc.Image = myImg
}

//     /**
//      * Draw paletted byte array bitmap data
//      *
//      * @param buffer  bitmap data
//      * @param rect    bitmap location and dimensions
//      * @param palette colour palette
//      * @param paletteSize number of colors in palette
//      */
// 	 func (enc *TightPngEncoding) drawBytesWithPalette( buffer []byte, rect *Rectangle,  palette []int, int paletteSize) {
// 		//create palette:
// 		var imgPalette []color.Color = make([]int, len(palette))
// 		for i:=0;len(palette);i++{
// 			col := color.RGBA{
// 				R:,G:,B:,A:0
// 			}
// 			imgPalette[i]=col
// 		 }

// 		//lock.lock();
// 		img:=image.Paletted{

// 		}
// 		// 2 colors
// 		thisWidth := enc.Image.Bounds().Max.Y
//         if paletteSize == 2 {
//             var  dx, dy, n int;
//              i := rect.y * thisWidth + rect.x
//              rowBytes := (rect.width + 7) / 8
//              var b byte;

//             for dy = 0; dy < rect.height; dy++ {
//                 for dx = 0; dx < rect.width / 8; dx++ {
//                     b = buffer[dy * rowBytes + dx];
//                     for n = 7; n >= 0; n-- {
// 						color := palette[b >> n & 1]
// 						enc.Image.(draw.Image).Set(0, 0, color.RGBA{R: tpx.R, G: tpx.G, B: tpx.B, A: 1})
//                         //pixels[i++] = palette[b >> n & 1];
//                     }
//                 }
//                 for n = 7; n >= 8 - rect.width % 8; n-- {
//                     pixels[i++] = palette[buffer[dy * rowBytes + dx] >> n & 1];
//                 }
//                 i += this.width - rect.width;
//             }
//         } else {
//             // 3..255 colors (assuming bytesPixel == 4).
//             int i = 0;
//             for (int ly = rect.y; ly < rect.y + rect.height; ++ly) {
//                 for (int lx = rect.x; lx < rect.x + rect.width; ++lx) {
//                     int pixelsOffset = ly * this.width + lx;
//                     pixels[pixelsOffset] = palette[buffer[i++] & 0xFF];
//                 }
//             }
//         }
//         //lock.unlock();
// 	}