Package dmd implements well-known formats for dot-matrix display (DMD) art.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
5.3 KiB

package dmd
import (
"encoding/binary"
"fmt"
"image/color"
"io"
"io/ioutil"
"os"
)
// Pin2DMDColoring contains a coloring description.
type Pin2DMDColoring struct {
Version uint8
Palette *Pin2DMDPalette
Palettes []*Pin2DMDPalette
Default int
Mappings map[uint32]*Pin2DMDMapping
Masks [][]byte
}
func (c *Pin2DMDColoring) GetPalette(i int) *Pin2DMDPalette {
if i < len(c.Palettes) {
return c.Palettes[i]
}
if c.Default < len(c.Palettes) {
return c.Palettes[c.Default]
}
return c.Palette
}
func (c *Pin2DMDColoring) readPalettes(r io.Reader) (err error) {
var palettes uint16
if err = binary.Read(r, binary.BigEndian, &palettes); err != nil {
return
}
// log.Printf("pal: reading %d palettes", palettes)
c.Palettes = make([]*Pin2DMDPalette, palettes)
for i := 0; i < int(palettes); i++ {
if c.Palettes[i], err = readPin2DMDPalette(r); err != nil {
return
}
// log.Printf("palette %d: %d colors, default:%t persistent:%t", i, len(c.Palettes[i].Colors), c.Palettes[i].IsDefault, c.Palettes[i].IsPersistent)
if c.Palette == nil && c.Palettes[i].IsDefault {
c.Palette = c.Palettes[i]
c.Default = i
}
}
if c.Palette == nil && len(c.Palettes) > 0 {
c.Palette = c.Palettes[0]
}
return
}
func (c *Pin2DMDColoring) readMappings(r io.Reader) (err error) {
var mappings uint16
if err = binary.Read(r, binary.BigEndian, &mappings); err != nil {
return
}
// log.Printf("pal: reading %d mappings", mappings)
c.Mappings = make(map[uint32]*Pin2DMDMapping)
for i := 0; i < int(mappings); i++ {
var m *Pin2DMDMapping
if m, err = readPin2DMDMapping(r); err != nil {
return
}
c.Mappings[m.Checksum] = m
}
return
}
func (c *Pin2DMDColoring) readMasks(r io.Reader) (err error) {
var masks byte
if err = binary.Read(r, binary.BigEndian, &masks); err != nil {
return
}
// log.Printf("pal: reading %d masks", masks)
var data []byte
if data, err = ioutil.ReadAll(r); err != nil {
return
}
maskBytes := len(data) / int(masks)
if maskBytes != 256 && maskBytes != 512 && maskBytes != 1536 {
return fmt.Errorf("dmd: %d bytes remaining for %d masks", maskBytes, masks)
}
c.Masks = make([][]byte, masks)
for i := 0; i < int(masks); i++ {
c.Masks[i], data = data[:maskBytes], data[maskBytes:]
}
return
}
/*
func (c *Pin2DMDColoring) FindMapping(p *Plane, reverse bool) *Pin2DMDMapping {
s := checkSum(p.Data, reverse)
if m, ok := c.Mappings[s]; ok {
// Unmasked CRC matched.
log.Printf("mapping: based on unmasked checksum %#08x", s)
return m
}
log.Printf("mapping: none for unmasked checksum %#08x", s)
for _, mask := range c.Masks {
s = checkSumWithMask(p.Data, mask, reverse)
if m, ok := c.Mappings[s]; ok {
log.Printf("mapping: based on masked checksum %#08x", s)
return m
}
}
return nil
}
*/
// LoadPin2DMDColoring loads *.pal Pin2DMD colorization files.
func LoadPin2DMDColoring(name string) (*Pin2DMDColoring, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer func() { _ = f.Close() }()
return DecodePin2DMDColoring(f)
}
// DecodePin2DMDColoring decodes palette mappings from the supplied Reader.
func DecodePin2DMDColoring(r io.Reader) (c *Pin2DMDColoring, err error) {
c = new(Pin2DMDColoring)
if err = binary.Read(r, binary.BigEndian, &c.Version); err != nil {
return
}
// log.Printf("pal: read version %d", c.Version)
if err = c.readPalettes(r); err != nil {
return nil, err
}
if err = c.readMappings(r); err != nil && err != io.EOF {
return nil, err
}
if err = c.readMasks(r); err != nil {
return nil, err
}
return c, nil
}
type Pin2DMDPalette struct {
Index uint16
Type uint8
IsDefault bool
IsPersistent bool
Colors color.Palette
}
func readPin2DMDPalette(r io.Reader) (p *Pin2DMDPalette, err error) {
p = new(Pin2DMDPalette)
if err = binary.Read(r, binary.BigEndian, &p.Index); err != nil {
return
}
var colors uint16
if err = binary.Read(r, binary.BigEndian, &colors); err != nil {
return
}
if err = binary.Read(r, binary.BigEndian, &p.Type); err != nil {
return
}
p.IsDefault = p.Type == 1 || p.Type == 2
p.IsPersistent = p.Type == 1
p.Colors = make(color.Palette, colors)
for i := 0; i < int(colors); i++ {
var rgb [3]byte
if _, err = io.ReadFull(r, rgb[:]); err != nil {
return
}
p.Colors[i] = color.RGBA{
R: rgb[0],
G: rgb[1],
B: rgb[2],
A: 0xff,
}
/*
if p.Colors[i], err = DecodeRGB24(r); err != nil {
return nil, err
}
*/
}
return p, nil
}
type SwitchMode byte
const (
SwitchModePalette SwitchMode = iota
SwitchModeReplace
SwitchModeColorMask
SwitchModeEvent
SwitchModeFollow
)
type Pin2DMDMapping struct {
Checksum uint32
Mode SwitchMode
PaletteIndex uint16
Duration uint32
Offset uint32
}
func readPin2DMDMapping(r io.Reader) (m *Pin2DMDMapping, err error) {
m = new(Pin2DMDMapping)
if err = binary.Read(r, binary.BigEndian, &m.Checksum); err != nil {
return
}
if err = binary.Read(r, binary.BigEndian, &m.Mode); err != nil {
return
}
if err = binary.Read(r, binary.BigEndian, &m.PaletteIndex); err != nil {
return
}
if m.Mode == SwitchModePalette {
err = binary.Read(r, binary.BigEndian, &m.Duration)
} else {
err = binary.Read(r, binary.BigEndian, &m.Offset)
}
return m, err
}
func (m Pin2DMDMapping) IsAnimation() bool {
switch m.Mode {
case SwitchModeReplace, SwitchModeColorMask, SwitchModeFollow:
return true
default:
return false
}
}