Browse Source

More refactoring

master 0.0.3
maze 8 months ago
parent
commit
9874d58e82
10 changed files with 246 additions and 177 deletions
  1. +3
    -0
      .gitignore
  2. +22
    -0
      bitmap/color/color.go
  3. +30
    -0
      bitmap/color/rgb24.go
  4. +34
    -0
      bitmap/color/rgba16.go
  5. +37
    -0
      bitmap/masked.go
  6. +111
    -0
      bitmap/rgba16.go
  7. +0
    -52
      color.go
  8. +3
    -1
      dmd1.go
  9. +3
    -121
      image.go
  10. +3
    -3
      vpin.go

+ 3
- 0
.gitignore View File

@ -0,0 +1,3 @@
.DS_Store
.idea
_attic

+ 22
- 0
bitmap/color/color.go View File

@ -0,0 +1,22 @@
package color
import "image/color"
// Type aliases.
type (
Color = color.Color
Model = color.Model
Palette = color.Palette
)
// Default colors.
var (
Opaque = color.Opaque
Transparent = color.Transparent
)
// Color models.
var (
RGBAModel = color.RGBAModel
RGBA16Model = color.RGBAModel
)

+ 30
- 0
bitmap/color/rgb24.go View File

@ -0,0 +1,30 @@
package color
import (
"encoding/binary"
"io"
)
// RGB24 is a color with 8-bit red, green and blue channels.
type RGB24 struct {
R, G, B uint8
}
// RGBA returns the alpha-premultiplied red, green, blue and alpha values
// for the color.
func (c RGB24) RGBA() (r, g, b, a uint32) {
r = uint32(c.R) | uint32(c.R)<<8
g = uint32(c.G) | uint32(c.G)<<8
b = uint32(c.B) | uint32(c.B)<<8
a = 0xffff
return
}
// DecodeRGB24 decodes one RGB24 color from the supplied Reader.
func DecodeRGB24(r io.Reader) (RGB24, error) {
var c RGB24
if err := binary.Read(r, binary.BigEndian, &c); err != nil {
return c, err
}
return c, nil
}

+ 34
- 0
bitmap/color/rgba16.go View File

@ -0,0 +1,34 @@
package color
import (
"encoding/binary"
"io"
"math/bits"
)
// RGBA16 is a color with 4-bit red, green, blue and alpha channels.
type RGBA16 uint16
// ByteSwap swaps the endianness of the color.
func (c *RGBA16) ByteSwap() {
*c = RGBA16(bits.Reverse16(uint16(*c)))
}
// RGBA returns the alpha-premultiplied red, green, blue and alpha values
// for the color.
func (c RGBA16) RGBA() (r, g, b, a uint32) {
r = uint32((c&0xf000)>>12) * 0x1111
g = uint32((c&0x0f00)>>8) * 0x1111
b = uint32((c&0x00f0)>>4) * 0x1111
a = uint32((c&0x000f)>>0) * 0x1111
return
}
// DecodeRGBA16 decodes one RGBA16 color from the supplied Reader.
func DecodeRGBA16(r io.Reader, byteOrder binary.ByteOrder) (RGBA16, error) {
var c RGBA16
if err := binary.Read(r, byteOrder, &c); err != nil {
return c, err
}
return c, nil
}

+ 37
- 0
bitmap/masked.go View File

@ -0,0 +1,37 @@
package bitmap
import (
"image"
"image/color"
)
// MaskedImage
type MaskedImage struct {
image.Paletted
Mask []byte
}
func (i MaskedImage) At(x, y int) color.Color {
if len(i.Palette) == 0 {
return nil
}
if !(image.Point{X: x, Y: y}.In(i.Rect)) {
return image.Transparent
}
o := i.PixOffset(x, y)
q := o / 8
r := o % 8
if i.Mask[q]&(1<<r) == 0 {
return color.Transparent
}
return i.Palette[i.Pix[o]]
}
func (i MaskedImage) HasMaskedPixels() bool {
for _, v := range i.Mask {
if v != 0xff {
return true
}
}
return false
}

+ 111
- 0
bitmap/rgba16.go View File

@ -0,0 +1,111 @@
package bitmap
import (
"encoding/binary"
"image"
"image/draw"
"io"
"maze.io/x/dmd/bitmap/color"
)
// RGBA16Image is a bitmap with 16-bit colors.
type RGBA16Image struct {
// Pix holds the image's pixels, as packed little-endian RGBA16 colors.
Pix []byte
// Rect is the image's bounds.
Rect image.Rectangle
}
// At returns the color of the pixel at (x, y).
func (i *RGBA16Image) At(x, y int) color.Color {
if !image.Pt(x, y).In(i.Rect) {
return color.Transparent
}
o := (y*i.Rect.Max.X + x) * 2
return color.RGBA16(binary.LittleEndian.Uint16(i.Pix[o:]))
}
// Set the color of the pixel at (x, y).
func (i *RGBA16Image) Set(x, y int, c color.Color) {
if !image.Pt(x, y).In(i.Rect) {
return
}
o := y*i.Rect.Dx()*2 + x*2
if v, ok := c.(color.RGBA16); ok {
binary.LittleEndian.PutUint16(i.Pix[o:], uint16(v))
} else {
r, g, b, a := c.RGBA()
v := uint16((r>>4)&0xf)<<12 | uint16((g>>4)&0xf)<<8 | uint16((b>>4)&0xf)<<4 | uint16((a>>4)&0xf)
binary.LittleEndian.PutUint16(i.Pix[o:], v)
}
}
// Bounds returns the domain for which At can return non-zero color.
func (i *RGBA16Image) Bounds() image.Rectangle {
return i.Rect
}
// ColorModel returns the color model for the image.
func (i *RGBA16Image) ColorModel() color.Model {
return color.RGBA16Model
}
const rgba4BitImageMagic = "4bit"
type rgba4BitImageHeader struct {
Magic [4]byte
Width, Height uint16
}
// ReadFrom reads an encoded image from the supplied Reader.
func (i *RGBA16Image) ReadFrom(r io.Reader) (n int64, err error) {
var header rgba4BitImageHeader
if err = binary.Read(r, binary.BigEndian, &header); err != nil {
return
}
i.Rect = image.Rect(0, 0, int(header.Width), int(header.Height))
var m int
i.Pix = make([]byte, i.Rect.Dx()*i.Rect.Dy()*2)
if m, err = io.ReadFull(r, i.Pix); err != nil {
return int64(binary.Size(header)) + int64(m), err
}
return int64(binary.Size(header)) + int64(m), nil
}
// WriteTo writes an encoded image to the supplied Writer.
func (i *RGBA16Image) WriteTo(w io.Writer) (n int64, err error) {
var header rgba4BitImageHeader
copy(header.Magic[:], rgba4BitImageMagic)
header.Width = uint16(i.Rect.Max.X)
header.Height = uint16(i.Rect.Max.Y)
if err = binary.Write(w, binary.BigEndian, header); err != nil {
return
}
var m int
if m, err = w.Write(i.Pix); err != nil {
return int64(binary.Size(header)) + int64(m), err
}
return int64(binary.Size(header)) + int64(m), nil
}
// NewRGBA16Image returns a new RGBA16Image with the given bounds.
func NewRGBA16Image(r image.Rectangle) *RGBA16Image {
return &RGBA16Image{
Rect: r,
Pix: make([]uint8, r.Dx()*r.Dy()*2),
}
}
// ToRGBA16Image converts the supplied image to RGBA16Image.
func ToRGBA16Image(i image.Image) *RGBA16Image {
if v, ok := i.(*RGBA16Image); ok {
return v
}
o := NewRGBA16Image(i.Bounds())
draw.Draw(o, o.Bounds(), i, image.Point{}, draw.Src)
return o
}

+ 0
- 52
color.go View File

@ -6,61 +6,9 @@ import (
"image/color"
"io"
"io/ioutil"
"math/bits"
"os"
)
// RGB24 is a color with 8-bit red, green and blue channels.
type RGB24 struct {
R, G, B uint8
}
// RGBA returns the alpha-premultiplied red, green, blue and alpha values
// for the color.
func (c RGB24) RGBA() (r, g, b, a uint32) {
r = uint32(c.R) | uint32(c.R)<<8
g = uint32(c.G) | uint32(c.G)<<8
b = uint32(c.B) | uint32(c.B)<<8
a = 0xffff
return
}
// DecodeRGB24 decodes one RGB24 color from the supplied Reader.
func DecodeRGB24(r io.Reader) (RGB24, error) {
var c RGB24
if err := binary.Read(r, binary.BigEndian, &c); err != nil {
return c, err
}
return c, nil
}
// RGBA16 is a color with 4-bit red, green, blue and alpha channels.
type RGBA16 uint16
// ByteSwap swaps the endianness of the color.
func (c *RGBA16) ByteSwap() {
*c = RGBA16(bits.Reverse16(uint16(*c)))
}
// RGBA returns the alpha-premultiplied red, green, blue and alpha values
// for the color.
func (c RGBA16) RGBA() (r, g, b, a uint32) {
r = uint32((c&0xf000)>>12) * 0x1111
g = uint32((c&0x0f00)>>8) * 0x1111
b = uint32((c&0x00f0)>>4) * 0x1111
a = uint32((c&0x000f)>>0) * 0x1111
return
}
// DecodeRGBA16 decodes one RGBA16 color from the supplied Reader.
func DecodeRGBA16(r io.Reader, byteOrder binary.ByteOrder) (RGBA16, error) {
var c RGBA16
if err := binary.Read(r, byteOrder, &c); err != nil {
return c, err
}
return c, nil
}
// Pin2DMDColoring contains a coloring description.
type Pin2DMDColoring struct {
Version uint8


+ 3
- 1
dmd1.go View File

@ -9,6 +9,8 @@ import (
"os"
"path/filepath"
"time"
"maze.io/x/dmd/bitmap"
)
const (
@ -136,7 +138,7 @@ func decodeDMD1Frame(r io.Reader, width, height int) (i image.Image, d time.Dura
return nil, 0, fmt.Errorf("dmd: expected DMD1 %q chunk size of %d, got %d", dmd1FrameDataChunk, need, chunk.size)
}
img := NewARGB4BitImage(image.Rect(0, 0, width, height))
img := bitmap.NewRGBA16Image(image.Rect(0, 0, width, height))
copy(img.Pix, chunk.Data)
// DMD1 sets the alpha to 0 if no alpha, or 1 if alpha channel.


+ 3
- 121
image.go View File

@ -1,133 +1,15 @@
package dmd
import (
"encoding/binary"
"image"
"image/color"
"image/draw"
"image/gif"
"io"
"time"
)
type RGBA4BitImage struct {
Rect image.Rectangle
Pix []byte
}
func (i RGBA4BitImage) At(x, y int) color.Color {
if !image.Pt(x, y).In(i.Rect) {
return color.Transparent
}
o := (y*i.Rect.Max.X + x) * 2
return RGBA16(binary.LittleEndian.Uint16(i.Pix[o:]))
}
func (i RGBA4BitImage) Set(x, y int, c color.Color) {
if !image.Pt(x, y).In(i.Rect) {
return
}
o := y*i.Rect.Dx()*2 + x*2
if v, ok := c.(RGBA16); ok {
binary.LittleEndian.PutUint16(i.Pix[o:], uint16(v))
} else {
r, g, b, a := c.RGBA()
v := uint16((r>>4)&0xf)<<12 | uint16((g>>4)&0xf)<<8 | uint16((b>>4)&0xf)<<4 | uint16((a>>4)&0xf)
binary.LittleEndian.PutUint16(i.Pix[o:], v)
}
}
func (i RGBA4BitImage) Bounds() image.Rectangle {
return i.Rect
}
func (RGBA4BitImage) ColorModel() color.Model {
return color.RGBAModel
}
const rgba4BitImageMagic = "4bit"
type rgba4BitImageHeader struct {
Magic [4]byte
Width, Height uint16
}
func (i *RGBA4BitImage) ReadFrom(r io.Reader) (n int64, err error) {
var header rgba4BitImageHeader
if err = binary.Read(r, binary.BigEndian, &header); err != nil {
return
}
i.Rect = image.Rect(0, 0, int(header.Width), int(header.Height))
var m int
i.Pix = make([]byte, i.Rect.Dx()*i.Rect.Dy()*2)
if m, err = io.ReadFull(r, i.Pix); err != nil {
return int64(binary.Size(header)) + int64(m), err
}
return int64(binary.Size(header)) + int64(m), nil
}
func (i *RGBA4BitImage) WriteTo(w io.Writer) (n int64, err error) {
var header rgba4BitImageHeader
copy(header.Magic[:], rgba4BitImageMagic)
header.Width = uint16(i.Rect.Max.X)
header.Height = uint16(i.Rect.Max.Y)
if err = binary.Write(w, binary.BigEndian, header); err != nil {
return
}
var m int
if m, err = w.Write(i.Pix); err != nil {
return int64(binary.Size(header)) + int64(m), err
}
return int64(binary.Size(header)) + int64(m), nil
}
func NewARGB4BitImage(r image.Rectangle) *RGBA4BitImage {
return &RGBA4BitImage{
Rect: r,
Pix: make([]uint8, r.Dx()*r.Dy()*2),
}
}
func ToARGB4BitImage(i image.Image) *RGBA4BitImage {
if v, ok := i.(*RGBA4BitImage); ok {
return v
}
o := NewARGB4BitImage(i.Bounds())
draw.Draw(o, o.Bounds(), i, image.Point{}, draw.Src)
return o
}
type MaskedImage struct {
image.Paletted
Mask []byte
}
func (i MaskedImage) At(x, y int) color.Color {
if len(i.Palette) == 0 {
return nil
}
if !(image.Point{X: x, Y: y}.In(i.Rect)) {
return image.Transparent
}
o := i.PixOffset(x, y)
q := o / 8
r := o % 8
if i.Mask[q]&(1<<r) == 0 {
return color.Transparent
}
return i.Palette[i.Pix[o]]
}
func (i MaskedImage) HasMaskedPixels() bool {
for _, v := range i.Mask {
if v != 0xff {
return true
}
}
return false
}
"maze.io/x/dmd/bitmap"
)
func EncodeToGIF(w io.Writer, a Animation) error {
g := &gif.GIF{
@ -163,7 +45,7 @@ func toPaletted(i image.Image) *image.Paletted {
}
return i
case *MaskedImage:
case *bitmap.MaskedImage:
if !i.HasMaskedPixels() {
return toPaletted(&i.Paletted)
}


+ 3
- 3
vpin.go View File

@ -5,11 +5,11 @@ import (
"encoding/binary"
"fmt"
"image"
"image/color"
"io"
"io/ioutil"
"time"
"maze.io/x/dmd/bitmap/color"
"maze.io/x/dmd/internal/heatshrink"
)
@ -184,7 +184,7 @@ func decodeVPINPalettesAndColors(r io.Reader, a *imageAnimation) (index int, err
a.palette = make(color.Palette, header.Colors)
for i := 0; i < int(header.Colors); i++ {
if a.palette[i], err = DecodeRGB24(r); err != nil {
if a.palette[i], err = color.DecodeRGB24(r); err != nil {
return
}
}
@ -235,7 +235,7 @@ func decodeVPINFrame(r io.Reader, width, height int, version int16, c *Pin2DMDCo
return
}
//frame := NewARGB4BitImage(image.Rect(0, 0, width, height))
//frame := NewRGBA16Image(image.Rect(0, 0, width, height))
frame = image.NewPaletted(image.Rect(0, 0, width, height), nil)
if version < 3 {
err = decodeVPINFramePlanes(r, frame, bits, header.PlaneSize, c)


Loading…
Cancel
Save