|
|
@ -10,6 +10,8 @@ import ( |
|
|
|
"path/filepath" |
|
|
|
"time" |
|
|
|
|
|
|
|
"maze.io/x/dmd/container" |
|
|
|
|
|
|
|
"maze.io/x/dmd/bitmap" |
|
|
|
) |
|
|
|
|
|
|
@ -25,7 +27,7 @@ const ( |
|
|
|
|
|
|
|
// DecodeDMD1 decodes one DMD1 animation (as used in RPI2DMD).
|
|
|
|
func DecodeDMD1(r io.Reader) (Animation, error) { |
|
|
|
var chunk DMD1Chunk |
|
|
|
var chunk container.DMD1Chunk |
|
|
|
if _, err := chunk.ReadFrom(r); err != nil { |
|
|
|
return nil, err |
|
|
|
} else if name := string(chunk.Name[:]); name != dmd1Magic { |
|
|
@ -51,7 +53,7 @@ func DecodeDMD1(r io.Reader) (Animation, error) { |
|
|
|
// log.Printf("dmd: DMD1 chunk %q of size %d", name, chunk.size)
|
|
|
|
switch name { |
|
|
|
case dmd1VersionChunk: |
|
|
|
if chunk.size != 4 { |
|
|
|
if chunk.Size != 4 { |
|
|
|
return nil, fmt.Errorf("dmd1: unexpected %q chunk size", name) |
|
|
|
} |
|
|
|
var version uint32 |
|
|
@ -71,7 +73,7 @@ func DecodeDMD1(r io.Reader) (Animation, error) { |
|
|
|
} |
|
|
|
|
|
|
|
func decodeDMD1Animation(r io.Reader, a *imageAnimation) error { |
|
|
|
var chunk DMD1Chunk |
|
|
|
var chunk container.DMD1Chunk |
|
|
|
for { |
|
|
|
if _, err := chunk.ReadFrom(r); err == io.EOF { |
|
|
|
return nil |
|
|
@ -117,14 +119,14 @@ func decodeDMD1Animation(r io.Reader, a *imageAnimation) error { |
|
|
|
} |
|
|
|
|
|
|
|
func decodeDMD1Frame(r io.Reader, width, height int) (i image.Image, d time.Duration, err error) { |
|
|
|
var chunk DMD1Chunk |
|
|
|
var chunk container.DMD1Chunk |
|
|
|
|
|
|
|
// TIME chunk.
|
|
|
|
if _, err = chunk.ReadFrom(r); err != nil { |
|
|
|
return |
|
|
|
} else if name := string(chunk.Name[:]); name != dmd1FrameTimeChunk { |
|
|
|
return nil, 0, fmt.Errorf("dmd: expected DMD1 %q chunk, got %q", dmd1FrameTimeChunk, name) |
|
|
|
} else if chunk.size != 4 { |
|
|
|
} else if chunk.Size != 4 { |
|
|
|
return nil, 0, fmt.Errorf("dmd: unexpected DMD1 %q chunk size", name) |
|
|
|
} |
|
|
|
d = time.Duration(binary.LittleEndian.Uint32(chunk.Data)) * time.Millisecond |
|
|
@ -134,8 +136,8 @@ func decodeDMD1Frame(r io.Reader, width, height int) (i image.Image, d time.Dura |
|
|
|
return |
|
|
|
} else if name := string(chunk.Name[:]); name != dmd1FrameDataChunk { |
|
|
|
return nil, 0, fmt.Errorf("dmd: expected DMD1 %q chunk, got %q", dmd1FrameDataChunk, name) |
|
|
|
} else if need := uint32(width*height*4) / 2; chunk.size < need { |
|
|
|
return nil, 0, fmt.Errorf("dmd: expected DMD1 %q chunk size of %d, got %d", dmd1FrameDataChunk, need, chunk.size) |
|
|
|
} else if need := uint32(width*height*4) / 2; chunk.Size < need { |
|
|
|
return nil, 0, fmt.Errorf("dmd: expected DMD1 %q chunk size of %d, got %d", dmd1FrameDataChunk, need, chunk.Size) |
|
|
|
} |
|
|
|
|
|
|
|
img := bitmap.NewRGBA16Image(image.Rect(0, 0, width, height)) |
|
|
@ -151,102 +153,5 @@ func decodeDMD1Frame(r io.Reader, width, height int) (i image.Image, d time.Dura |
|
|
|
img.Pix[i] |= 0x0f |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
img := image.NewRGBA(image.Rect(0, 0, width, height)) |
|
|
|
for y := 0; y < height; y++ { |
|
|
|
o := y * width * 2 |
|
|
|
for x := 0; x < width; x, o = x+1, o+2 { |
|
|
|
c := binary.LittleEndian.Uint16(chunk.Data[o:]) |
|
|
|
r := uint8((c & 0xf000) >> 12) |
|
|
|
r |= r << 4 |
|
|
|
g := uint8((c & 0x0f00) >> 8) |
|
|
|
g |= g << 4 |
|
|
|
b := uint8((c & 0x00f0) >> 4) |
|
|
|
b |= b << 4 |
|
|
|
img.Set(x, y, color.RGBA{R: r, G: g, B: b, A: 0xff}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
*/ |
|
|
|
/* |
|
|
|
for y := 0; y < height; y++ { |
|
|
|
o := y * width |
|
|
|
for x := 0; x < width; x, o = x+1, o+1 { |
|
|
|
// Pixel data is stored in nibbles, so the nth pixel starts at the n*3rd nibble.
|
|
|
|
// 01 23 45 67 89 ..
|
|
|
|
// RG BR GB RG BR GB
|
|
|
|
n := o * 3 // nibble
|
|
|
|
if n&1 == 0 { |
|
|
|
// Starts at byte.
|
|
|
|
n /= 2 |
|
|
|
r := chunk.Data[n] >> 4 |
|
|
|
r |= r << 4 |
|
|
|
g := chunk.Data[n] & 0xf |
|
|
|
g |= g << 4 |
|
|
|
b := chunk.Data[n+1] >> 4 |
|
|
|
b |= b << 4 |
|
|
|
img.Set(x, y, color.RGBA{R: r, G: g, B: b, A: 0xff}) |
|
|
|
} else { |
|
|
|
// Starts at nibble.
|
|
|
|
n /= 2 |
|
|
|
r := chunk.Data[n] & 0xf |
|
|
|
r |= r << 4 |
|
|
|
g := chunk.Data[n+1] >> 4 |
|
|
|
g |= g << 4 |
|
|
|
b := chunk.Data[n+1] & 0xf |
|
|
|
b |= b << 4 |
|
|
|
img.Set(x, y, color.RGBA{R: r, G: g, B: b, A: 0xff}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
return img, d, nil |
|
|
|
} |
|
|
|
|
|
|
|
type DMD1Chunk struct { |
|
|
|
Name [4]byte |
|
|
|
size uint32 |
|
|
|
Data []byte |
|
|
|
} |
|
|
|
|
|
|
|
func (chunk *DMD1Chunk) ReadFrom(r io.Reader) (n int64, err error) { |
|
|
|
if err = binary.Read(r, binary.LittleEndian, &chunk.Name); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
if err = binary.Read(r, binary.LittleEndian, &chunk.size); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
chunk.Data = make([]byte, chunk.size) |
|
|
|
if chunk.size > 0 { |
|
|
|
if _, err = io.ReadFull(r, chunk.Data); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
return 8 + int64(chunk.size), nil |
|
|
|
} |
|
|
|
|
|
|
|
func (chunk *DMD1Chunk) WriteTo(w io.Writer) (n int64, err error) { |
|
|
|
if err = binary.Write(w, binary.LittleEndian, chunk.Name); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
n += 4 |
|
|
|
chunk.size = uint32(len(chunk.Data)) |
|
|
|
if err = binary.Write(w, binary.LittleEndian, chunk.size); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
n += 4 |
|
|
|
if chunk.size > 0 { |
|
|
|
var m int |
|
|
|
if m, err = w.Write(chunk.Data); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
n += int64(m) |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
var ( |
|
|
|
_ io.ReaderFrom = (*DMD1Chunk)(nil) |
|
|
|
_ io.WriterTo = (*DMD1Chunk)(nil) |
|
|
|
) |