initj
This commit is contained in:
parent
6169bca983
commit
fca2b77d7c
2 changed files with 282 additions and 0 deletions
225
esi.go
Normal file
225
esi.go
Normal file
|
@ -0,0 +1,225 @@
|
|||
package esi
|
||||
|
||||
/*
|
||||
> Extremely Simple Image file format <
|
||||
>------------------------------------------------------------------------------------------<
|
||||
> Designed for databending or glitching
|
||||
> Has very little fancy features that could cause problems with decoding
|
||||
> Decoder is designed to assume, without any penalties if it assumes wrong
|
||||
> Width parameter is assumed to be the square root of the length of the data, rounded up
|
||||
> Color format is assumed to be 8 bit color
|
||||
> Can support grayscale or color in a variety of bit depths
|
||||
> There is no data between or around pixels, or at the end of the file
|
||||
> It is literally a sequence of bits after the minimal header
|
||||
> This should prevent any issues with databending
|
||||
> Header is encoded with 16 bits for width, 1 indexed, for a maximum width of 65536 pixels
|
||||
> All data is big-endian encoded
|
||||
> This is followed by the following 5 bits which is followed by 11 ignored bits:
|
||||
> | 0
|
||||
> | 0000 1 bit black and white
|
||||
> | #### 4 bits encoding bit depth, 1 indexed
|
||||
> -------------------------------
|
||||
> | 1
|
||||
> | #### 4 bits encoding bit depth per channel, 1 indexed
|
||||
>
|
||||
> Sample header:
|
||||
> | 01100101 01110011 01101001 00110001 ( esi1 in ascii binary )
|
||||
> | 00000000 00010000 10100000 00000000
|
||||
>
|
||||
> | 00000000
|
||||
> | 00010000 width of 32
|
||||
> | 1 color
|
||||
> | 0100 8 bit depth
|
||||
> | 000 00000000 ignored bits
|
||||
> | ( to ease databending the entire image )
|
||||
> | ( without affecting the header )
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
errImageInvalid = errors.New("esi: image is invalid")
|
||||
errNotESI = errors.New("esi: not an esi")
|
||||
)
|
||||
|
||||
func init() {
|
||||
image.RegisterFormat("esi", "esi1", Decode, DecodeConfig)
|
||||
}
|
||||
|
||||
func Decode(r io.Reader) (o image.Image, err error) {
|
||||
buf := bytes.Buffer{}
|
||||
io.Copy(&buf, r)
|
||||
|
||||
cfgBuf := bytes.NewBuffer(buf.Bytes())
|
||||
cfg, err := DecodeConfig(cfgBuf)
|
||||
if err != nil {
|
||||
return o, err
|
||||
}
|
||||
|
||||
log.Println(cfg)
|
||||
|
||||
bounds := image.Rect(0, 0, cfg.Width, cfg.Height)
|
||||
|
||||
log.Println(bounds)
|
||||
|
||||
img := image.NewRGBA(bounds)
|
||||
|
||||
buf.Next(8)
|
||||
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
|
||||
r, e1 := buf.ReadByte()
|
||||
g, e2 := buf.ReadByte()
|
||||
b, e3 := buf.ReadByte()
|
||||
|
||||
for _, err := range []error{e1, e2, e3} {
|
||||
// log.Println(err)
|
||||
if err != nil && err != io.EOF {
|
||||
return o, err
|
||||
}
|
||||
}
|
||||
|
||||
c := color.RGBA{
|
||||
R: r,
|
||||
G: g,
|
||||
B: b,
|
||||
A: 255,
|
||||
}
|
||||
|
||||
img.SetRGBA(x, y, c)
|
||||
// log.Println(x, y, c)
|
||||
// log.Println(img.At(x, y).RGBA())
|
||||
}
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func DecodeConfig(r io.Reader) (cfg image.Config, err error) {
|
||||
// pull first 8 bytes out...
|
||||
buf := bytes.Buffer{}
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
buf.ReadFrom(r)
|
||||
|
||||
// check first 4 == esi1
|
||||
magic := buf.Next(4)
|
||||
if !bytes.Equal(magic, []byte("esi1")) {
|
||||
return cfg, errNotESI
|
||||
}
|
||||
|
||||
// read headers
|
||||
header := buf.Next(4)
|
||||
w := binary.BigEndian.Uint16(header[0:2]) // width is first two bytes (16 bit)
|
||||
cfg.Width = int(w)
|
||||
|
||||
cfg.ColorModel = GetColorModel(header[2])
|
||||
|
||||
// calculate length as a product of width / length of reader
|
||||
|
||||
size := buf.Len()
|
||||
cfg.Height = size / 3 / cfg.Width
|
||||
|
||||
// log.Println("size w/o headers", size)
|
||||
// log.Println("height:", cfg.Height)
|
||||
// log.Println("width:", cfg.Width)
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func Encode(w io.Writer, img image.Image) error {
|
||||
bounds := img.Bounds()
|
||||
|
||||
_, err := w.Write([]byte("esi1"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = binary.Write(w, binary.BigEndian, uint16(bounds.Max.X))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cm := img.ColorModel()
|
||||
cc := true
|
||||
cw := 8
|
||||
|
||||
switch cm {
|
||||
case color.GrayModel:
|
||||
fallthrough
|
||||
case color.Gray16Model:
|
||||
cc = false
|
||||
}
|
||||
|
||||
switch cm {
|
||||
case color.RGBA64Model:
|
||||
fallthrough
|
||||
case color.Gray16Model:
|
||||
cw = 16
|
||||
}
|
||||
|
||||
configByte := byte(0)
|
||||
|
||||
if cc {
|
||||
configByte |= 128
|
||||
}
|
||||
|
||||
if cw == 16 {
|
||||
configByte |= 64
|
||||
} else {
|
||||
configByte |= 32
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte{configByte, 0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for y := 0; y < bounds.Max.Y; y++ {
|
||||
for x := 0; x < bounds.Max.X; x++ {
|
||||
r, g, b, _ := img.At(x, y).RGBA()
|
||||
_, err = w.Write([]byte{byte(r / 257), byte(g / 257), byte(b / 257)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetColorModel takes the encoding data byte of an ESI header,
|
||||
// and outputs the corresponding color.Model
|
||||
func GetColorModel(b byte) (m color.Model) {
|
||||
cc := b&(1<<7)>>7 == 1 // color or not is first bit
|
||||
depth := b &^ 128 >> 2 // unshift first bit, read next few
|
||||
|
||||
return color.RGBAModel
|
||||
|
||||
if cc {
|
||||
if depth == 16 {
|
||||
m = color.RGBA64Model
|
||||
} else {
|
||||
m = color.RGBAModel
|
||||
}
|
||||
} else {
|
||||
if depth == 16 {
|
||||
m = color.Gray16Model
|
||||
} else {
|
||||
m = color.GrayModel
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
57
esiconv/main.go
Normal file
57
esiconv/main.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/kayteh/esi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
convPath := os.Args[1]
|
||||
outPath := os.Args[2]
|
||||
|
||||
fin, err := os.Open(convPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
fout, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
img, _, err := image.Decode(fin)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
fin.Close()
|
||||
|
||||
ext := path.Ext(outPath)
|
||||
switch ext[1:] {
|
||||
case "esi":
|
||||
err = esi.Encode(fout, img)
|
||||
case "png":
|
||||
err = png.Encode(fout, img)
|
||||
case "jpg":
|
||||
fallthrough
|
||||
case "jpeg":
|
||||
err = jpeg.Encode(fout, img, nil)
|
||||
case "gif":
|
||||
err = gif.Encode(fout, img, nil)
|
||||
default:
|
||||
log.Fatalf("extension %s unknown", ext)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
fout.Close()
|
||||
}
|
Loading…
Add table
Reference in a new issue