367 lines · 8.0 KB · Go
RawDownload
package tga import (	"bytes"	"encoding/binary"	"errors"	"fmt"	"image"	"image/color"	"io") type tga struct {	r             *bytes.Reader	raw           rawHeader	isPaletted    bool	hasAlpha      bool	width         int	height        int	pixelSize     int	palette       []byte	paletteLength int	ColorModel    color.Model	tmp           [4]byte	decode        func(tga *tga, out []byte) (err error)} var (	ErrAlphaSize    = errors.New("tga: invalid alpha size")	ErrFormat       = errors.New("tga: invalid format")	ErrPaletteIndex = errors.New("tga: palette index out of range")) // Decode decodes a TARGA image.func Decode(r io.Reader) (outImage image.Image, err error) {	var tga tga	var data bytes.Buffer 	if _, err = data.ReadFrom(r); err != nil {		return	} 	tga.r = bytes.NewReader(data.Bytes()) 	if err = tga.getHeader(); err != nil {		return	} 	// skip header	if _, err = tga.r.Seek(int64(tgaRawHeaderSize+tga.raw.IdLength), 0); err != nil {		return	} 	if tga.isPaletted {		// read palette		entrySize := int((tga.raw.PaletteBPP + 1) >> 3)		tga.paletteLength = int(tga.raw.PaletteLength - tga.raw.PaletteFirst)		tga.palette = make([]byte, entrySize*tga.paletteLength) 		// skip to colormap		if _, err = tga.r.Seek(int64(entrySize)*int64(tga.raw.PaletteFirst), 1); err != nil {			return		} 		if _, err = io.ReadFull(tga.r, tga.palette); err != nil {			return		}	} 	rect := image.Rect(0, 0, tga.width, tga.height)	var pixels []byte 	// choose a right color model	if tga.ColorModel == color.NRGBAModel {		im := image.NewNRGBA(rect)		outImage = im		pixels = im.Pix	} else {		im := image.NewRGBA(rect)		outImage = im		pixels = im.Pix	} 	if err = tga.decode(&tga, pixels); err == nil {		tga.flip(pixels)	} 	return} // DecodeConfig decodes a header of TARGA image and returns its configuration.func DecodeConfig(r io.Reader) (cfg image.Config, err error) {	var tga tga	var data bytes.Buffer 	if _, err = data.ReadFrom(r); err != nil {		return	} 	tga.r = bytes.NewReader(data.Bytes()) 	if err = tga.getHeader(); err == nil {		cfg = image.Config{			ColorModel: tga.ColorModel,			Width:      tga.width,			Height:     tga.height,		}	} 	return} func init() {	image.RegisterFormat("tga", "", Decode, DecodeConfig)} // applyExtensions reads extensions section (if it exists) and parses attribute type.func (tga *tga) applyExtensions() (err error) {	var rawFooter rawFooter 	if _, err = tga.r.Seek(int64(-tgaRawFooterSize), 2); err != nil {		return	} else if err = binary.Read(tga.r, binary.LittleEndian, &rawFooter); err != nil {		return	} else if bytes.Equal(rawFooter.Signature[:], tgaSignature[:]) && rawFooter.ExtAreaOffset != 0 {		offset := int64(rawFooter.ExtAreaOffset + extAreaAttrTypeOffset) 		var n int64		var t byte 		if n, err = tga.r.Seek(offset, 0); err != nil || n != offset {			return		} else if t, err = tga.r.ReadByte(); err != nil {			return		} else if t == attrTypeAlpha {			// alpha			tga.hasAlpha = true		} else if t == attrTypePremultipliedAlpha {			// premultiplied alpha			tga.hasAlpha = true			tga.ColorModel = color.RGBAModel		} else {			// attribute is not an alpha channel value, ignore it			tga.hasAlpha = false		}	} 	return} // decodeRaw decodes a raw (uncompressed) data.func decodeRaw(tga *tga, out []byte) (err error) {	for i := 0; i < len(out) && err == nil; i += 4 {		err = tga.getPixel(out[i:])	} 	return} // decodeRLE decodes run-length encoded data.func decodeRLE(tga *tga, out []byte) (err error) {	size := tga.width * tga.height * 4 	for i := 0; i < size && err == nil; {		var b byte 		if b, err = tga.r.ReadByte(); err != nil {			break		} 		count := uint(b) 		if count&(1<<7) != 0 {			// encoded packet			count &= ^uint(1 << 7) 			if err = tga.getPixel(tga.tmp[:]); err == nil {				for count++; count > 0 && i < size; count-- {					copy(out[i:], tga.tmp[:])					i += 4				}			}		} else {			// raw packet			for count++; count > 0 && i < size && err == nil; count-- {				err = tga.getPixel(out[i:])				i += 4			}		}	} 	return} // flip flips pixels of image based on its origin.func (tga *tga) flip(out []byte) {	flipH := tga.raw.Flags&flagOriginRight != 0	flipV := tga.raw.Flags&flagOriginTop == 0	rowSize := tga.width * 4 	if flipH {		for y := 0; y < tga.height; y++ {			for x, offset := 0, y*rowSize; x < tga.width/2; x++ {				a := out[offset+x*4:]				b := out[offset+(tga.width-x-1)*4:] 				a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3] = b[0], b[1], b[2], b[3], a[0], a[1], a[2], a[3]			}		}	} 	if flipV {		for y := 0; y < tga.height/2; y++ {			for x := 0; x < tga.width; x++ {				a := out[y*rowSize+x*4:]				b := out[(tga.height-y-1)*rowSize+x*4:] 				a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3] = b[0], b[1], b[2], b[3], a[0], a[1], a[2], a[3]			}		}	}} // getHeader reads and validates TGA header.func (tga *tga) getHeader() (err error) {	if err = binary.Read(tga.r, binary.LittleEndian, &tga.raw); err != nil {		return	} 	if tga.raw.ImageType&imageTypeFlagRLE != 0 {		tga.decode = decodeRLE	} else {		tga.decode = decodeRaw	} 	tga.raw.ImageType &= imageTypeMask	alphaSize := tga.raw.Flags & flagAlphaSizeMask 	if alphaSize != 0 && alphaSize != 1 && alphaSize != 8 {		err = ErrAlphaSize		return	} 	tga.hasAlpha = ((alphaSize != 0 || tga.raw.BPP == 32) ||		(tga.raw.ImageType == imageTypeMonoChrome && tga.raw.BPP == 16) ||		(tga.raw.ImageType == imageTypePaletted && tga.raw.PaletteBPP == 32)) 	tga.width = int(tga.raw.Width)	tga.height = int(tga.raw.Height)	tga.pixelSize = int(tga.raw.BPP) >> 3 	// default is NOT premultiplied alpha model	tga.ColorModel = color.NRGBAModel 	if err = tga.applyExtensions(); err != nil {		return	} 	var formatIsInvalid bool 	switch tga.raw.ImageType {	case imageTypePaletted:		formatIsInvalid = (tga.raw.PaletteType != 1 ||			tga.raw.BPP != 8 ||			tga.raw.PaletteFirst >= tga.raw.PaletteLength ||			(tga.raw.PaletteBPP != 15 && tga.raw.PaletteBPP != 16 && tga.raw.PaletteBPP != 24 && tga.raw.PaletteBPP != 32))		tga.isPaletted = true 	case imageTypeTrueColor:		formatIsInvalid = (tga.raw.BPP != 32 &&			tga.raw.BPP != 16 &&			(tga.raw.BPP != 24 || tga.hasAlpha)) 	case imageTypeMonoChrome:		formatIsInvalid = ((tga.hasAlpha && tga.raw.BPP != 16) ||			(!tga.hasAlpha && tga.raw.BPP != 8)) 	default:		err = fmt.Errorf("tga: unknown image type %d", tga.raw.ImageType)	} 	if err == nil && formatIsInvalid {		err = ErrFormat	} 	return} func (tga *tga) getPixel(dst []byte) (err error) {	var R, G, B, A uint8 = 0xff, 0xff, 0xff, 0xff	src := tga.tmp 	if _, err = io.ReadFull(tga.r, src[0:tga.pixelSize]); err != nil {		return	} 	switch tga.pixelSize {	case 4:		if tga.hasAlpha {			A = src[3]		}		fallthrough 	case 3:		B, G, R = src[0], src[1], src[2] 	case 2:		if tga.raw.ImageType == imageTypeMonoChrome {			B, G, R = src[0], src[0], src[0] 			if tga.hasAlpha {				A = src[1]			}		} else {			word := uint16(src[0]) | (uint16(src[1]) << 8)			B, G, R = wordToBGR(word) 			if tga.hasAlpha && (word&(1<<15)) == 0 {				A = 0			}		} 	case 1:		if tga.isPaletted {			index := int(src[0]) 			if int(index) >= tga.paletteLength {				return ErrPaletteIndex			} 			var m int 			if tga.raw.PaletteBPP == 24 {				m = index * 3				B, G, R = tga.palette[m+0], tga.palette[m+1], tga.palette[m+2]			} else if tga.raw.PaletteBPP == 32 {				m = index * 4				B, G, R = tga.palette[m+0], tga.palette[m+1], tga.palette[m+2] 				if tga.hasAlpha {					A = tga.palette[m+3]				}			} else if tga.raw.PaletteBPP == 16 {				m = index * 2				word := uint16(tga.palette[m+0]) | (uint16(tga.palette[m+1]) << 8)				B, G, R = wordToBGR(word)			}		} else {			B, G, R = src[0], src[0], src[0]		}	} 	dst[0], dst[1], dst[2], dst[3] = R, G, B, A 	return nil} // wordToBGR converts 15-bit color to BGRfunc wordToBGR(word uint16) (B, G, R uint8) {	B = uint8((word >> 0) & 31)	B = uint8((B << 3) + (B >> 2))	G = uint8((word >> 5) & 31)	G = uint8((G << 3) + (G >> 2))	R = uint8((word >> 10) & 31)	R = uint8((R << 3) + (R >> 2))	return}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t