~shabbyrobe/bumpy

ea3829deee96818f399ed6674eeab651678d83a7 — Blake Williams 11 months ago 15f52b9 master
Decode as DIB
M bitmapcore.go => bitmapcore.go +4 -3
@@ 3,7 3,7 @@ package bumpy
// BITMAPCOREHEADER
//
// The Windows 2.x BITMAPCOREHEADER differs from the OS/2 1.x BITMAPCOREHEADER in the one
// detail that the image width and height fields are signed integers,not unsigned.
// detail that the image width and height fields are signed integers, not unsigned.
type BitmapCore struct {
	DIBSize DIBSize



@@ 20,8 20,9 @@ type BitmapCore struct {

var _ Header = BitmapCore{}

func (c BitmapCore) VersionName() string { return "BitmapCore" }
func (c BitmapCore) Hint() Hint          { return Windows }
func (c BitmapCore) BitmapDIBSize() DIBSize { return c.DIBSize }
func (c BitmapCore) VersionName() string    { return "BitmapCore" }
func (c BitmapCore) Hint() Hint             { return Windows }

func (c BitmapCore) Regions(fh FileHeader) (r Regions) {
	r.PixData.Start = int64(fh.PixOffset)

M bitmapinfo.go => bitmapinfo.go +3 -2
@@ 31,8 31,9 @@ type BitmapInfo struct {

var _ Header = BitmapInfo{}

func (c BitmapInfo) VersionName() string { return "BitmapInfo" }
func (c BitmapInfo) Hint() Hint          { return Windows }
func (c BitmapInfo) VersionName() string    { return "BitmapInfo" }
func (c BitmapInfo) Hint() Hint             { return Windows }
func (c BitmapInfo) BitmapDIBSize() DIBSize { return c.DIBSize }

func (c BitmapInfo) LoadColorTable(fh FileHeader, data []byte) (n int, pal Palette, err error) {
	if c.PaletteSize <= 0 {

M bitmapv4.go => bitmapv4.go +3 -2
@@ 77,8 77,9 @@ type BitmapV4 struct {

var _ Header = BitmapV4{}

func (c BitmapV4) VersionName() string { return "BitmapV4" }
func (c BitmapV4) Hint() Hint          { return Windows }
func (c BitmapV4) VersionName() string    { return "BitmapV4" }
func (c BitmapV4) Hint() Hint             { return Windows }
func (c BitmapV4) BitmapDIBSize() DIBSize { return c.DIBSize }

func (c BitmapV4) LoadColorTable(fh FileHeader, data []byte) (n int, pal Palette, err error) {
	return loadV4V5ColorTable(c.Compression, c.BitsPerPixel, c.PaletteSize, data)

M bitmapv5.go => bitmapv5.go +3 -2
@@ 154,8 154,9 @@ type BitmapV5 struct {

var _ Header = BitmapV5{}

func (c BitmapV5) VersionName() string { return "BitmapV5" }
func (c BitmapV5) Hint() Hint          { return Windows }
func (c BitmapV5) VersionName() string    { return "BitmapV5" }
func (c BitmapV5) Hint() Hint             { return Windows }
func (c BitmapV5) BitmapDIBSize() DIBSize { return c.DIBSize }

func (c BitmapV5) Regions(fh FileHeader) (r Regions) {
	// FIXME: BI_BITFIELDS may confound this calculation:

M cmd/bumpy/cmd_check.go => cmd/bumpy/cmd_check.go +1 -1
@@ 76,7 76,7 @@ func (cmd *checkCommand) Run(ctx cmdy.Context) error {
			return err
		}

		_, bmp, err := bumpy.DecodeBitmapHeader(bts, bumpy.Windows)
		_, bmp, err := bumpy.DecodeBitmapFileHeader(bts, bumpy.Windows)
		if err != nil {
			fmt.Printf("\x1b[091mFAIL\x1b[0m: %s: %s\n", file, err)
		} else {

A cmd/bumpy/cmd_dib.go => cmd/bumpy/cmd_dib.go +94 -0
@@ 0,0 1,94 @@
package main

import (
	"fmt"
	"image/color"

	"github.com/davecgh/go-spew/spew"
	"github.com/shabbyrobe/cmdy"
	"github.com/shabbyrobe/cmdy/arg"
	"github.com/shabbyrobe/cmdy/cmdyutil"
	"go.shabbyrobe.org/bumpy"
)

type dibCommand struct {
	file string
}

func (cmd *dibCommand) Help() cmdy.Help { return cmdy.Synopsis("Header") }

func (cmd *dibCommand) Configure(flags *cmdy.FlagSet, args *arg.ArgSet) {
	args.String(&cmd.file, "file", "File")
}

func (cmd *dibCommand) Run(ctx cmdy.Context) error {
	bts, err := cmdyutil.ReadStdinOrFile(ctx, cmd.file, cmdyutil.HyphenStdin)
	if err != nil {
		return err
	}

	return cmd.runDIBBmp(ctx, bts)
}

func (cmd *dibCommand) runDIBBmp(ctx cmdy.Context, bts []byte) error {
	_, bmp, err := bumpy.DecodeBitmapDIBHeader(bts, bumpy.Windows)
	if err != nil {
		return err
	}

	fmt.Println(bmp.VersionName())
	spew.Dump(bmp)

	regions := bmp.Regions(bumpy.FileHeader{})
	spew.Dump(regions)

	ctab := regions.ColorTable
	if ctab.Set() {
		fmt.Println()

		var p color.Palette
		var pal bumpy.Palette
		if bsl, ok := ctab.Slice(bts); ok {
			_, pal, err = bmp.LoadColorTable(bumpy.FileHeader{}, bsl)
			if err != nil {
				return err
			}
			p = pal.Palette()
		}

		var withHex = false
		var cols int
		var prn func(idx int, r, g, b, a uint32)

		if withHex {
			cols = 6
			prn = func(idx int, r, g, b, a uint32) {
				fmt.Printf("%3[1]d:\033[48;2;%[2]d;%[3]d;%[4]dm \033[0m #%02[2]x%02[3]x%02[4]x  ", idx, r>>8, g>>8, b>>8)
			}
			if hasAlpha(p) {
				cols = 5
				prn = func(idx int, r, g, b, a uint32) {
					fmt.Printf("%3d: #%02x%02x%02x%02x  ", idx, r>>8, g>>8, b>>8, a>>8)
				}
			}
		} else {
			cols = 14
			prn = func(idx int, r, g, b, a uint32) {
				fmt.Printf("%3[1]d:\033[48;2;%[2]d;%[3]d;%[4]dm \033[0m ", idx, r>>8, g>>8, b>>8)
			}
		}

		rows := (len(p) + cols - 1) / cols

		fmt.Printf("Palette: %T (%x,%x)\n", pal, ctab.Start, ctab.End)
		for row := 0; row < rows; row++ {
			for j := row; j < len(p); j += rows {
				r, g, b, a := p[j].RGBA()
				prn(j, r, g, b, a)
			}
			fmt.Println()
		}
	}

	return nil
}

M cmd/bumpy/cmd_hdr.go => cmd/bumpy/cmd_hdr.go +9 -7
@@ 76,7 76,7 @@ func (cmd *hdrCommand) runIcon(ctx cmdy.Context, bts []byte) error {
}

func (cmd *hdrCommand) runBmp(ctx cmdy.Context, bts []byte) error {
	_, bmp, err := bumpy.DecodeBitmapHeader(bts, bumpy.Windows)
	_, bmp, err := bumpy.DecodeBitmapFileHeader(bts, bumpy.Windows)
	if err != nil {
		return err
	}


@@ 89,14 89,16 @@ func (cmd *hdrCommand) runBmp(ctx cmdy.Context, bts []byte) error {
	if ctab.Set() {
		fmt.Println()

		bsl, _ := ctab.Slice(bts)
		_, pal, err := bmp.Header.LoadColorTable(bmp.FileHeader, bsl)
		if err != nil {
			return err
		var p color.Palette
		var pal bumpy.Palette
		if bsl, ok := ctab.Slice(bts); ok {
			_, pal, err = bmp.Header.LoadColorTable(bmp.FileHeader, bsl)
			if err != nil {
				return err
			}
			p = pal.Palette()
		}

		p := pal.Palette()

		var withHex = false
		var cols int
		var prn func(idx int, r, g, b, a uint32)

M cmd/bumpy/cmd_region.go => cmd/bumpy/cmd_region.go +1 -1
@@ 35,7 35,7 @@ func (cmd *regionCommand) Run(ctx cmdy.Context) error {
		return fmt.Errorf("works on bitmaps only")
	}

	_, hdr, err := bumpy.DecodeBitmapHeader(bts, 0)
	_, hdr, err := bumpy.DecodeBitmapFileHeader(bts, 0)
	if err != nil {
		return err
	}

M cmd/bumpy/cmd_scour.go => cmd/bumpy/cmd_scour.go +18 -2
@@ 12,6 12,7 @@ import (
type scourCommand struct {
	start int
	end   int
	dib   bool
	file  string
}



@@ 22,6 23,7 @@ func (cmd *scourCommand) Help() cmdy.Help {
func (cmd *scourCommand) Configure(flags *cmdy.FlagSet, args *arg.ArgSet) {
	flags.IntVar(&cmd.start, "start", -1, "Start index (inclusive)")
	flags.IntVar(&cmd.end, "end", -1, "End index (exclusive)")
	flags.BoolVar(&cmd.dib, "dib", true, "Look for DIBs as well as BMPs (may have more false positives)")
	args.String(&cmd.file, "file", "File name ('-' for stdin)")
}



@@ 50,15 52,29 @@ func (cmd *scourCommand) Run(ctx cmdy.Context) error {
	}

	for i := start; i < end; i++ {
		_, hdr, err := bumpy.DecodeBitmapHeader(bts[i:], 0)
		_, hdr, err := bumpy.DecodeBitmapFileHeader(bts[i:], 0)
		if err != nil {
			continue
		} else if hdr.FileHeader.Kind.Valid() {
			fmt.Printf("bitmap type %q of size %d detected at offset %d\n",
			fmt.Printf("bitmap type %q (%q) of size %d detected at offset %d\n",
				hdr.Header.VersionName(),
				hdr.FileHeader.Kind,
				hdr.FileHeader.FileSize,
				i)
		}

		if cmd.dib {
			_, dib, err := bumpy.DecodeBitmapDIBHeader(bts[i:], 0)
			if err != nil {
				continue
			} else if dib.BitmapDIBSize().Valid() {
				fmt.Printf("dib type %s (%d) of size %d detected at offset %d\n",
					dib.VersionName(),
					dib.BitmapDIBSize(),
					hdr.FileHeader.FileSize,
					i)
			}
		}
	}

	return nil

M cmd/bumpy/main.go => cmd/bumpy/main.go +1 -0
@@ 18,6 18,7 @@ func run() error {
	bld := func() cmdy.Command {
		return cmdy.NewGroup("bumpy", cmdy.Builders{
			"check":  func() cmdy.Command { return &checkCommand{} },
			"dib":    func() cmdy.Command { return &dibCommand{} },
			"hdr":    func() cmdy.Command { return &hdrCommand{} },
			"region": func() cmdy.Command { return &regionCommand{} },
			"scour":  func() cmdy.Command { return &scourCommand{} },

M header.go => header.go +1 -0
@@ 37,6 37,7 @@ type Header interface {
	Hint() Hint
	Regions(FileHeader) Regions
	LoadColorTable(fh FileHeader, data []byte) (n int, pal Palette, err error)
	BitmapDIBSize() DIBSize
}

type HeaderKind uint16

M os22x.go => os22x.go +3 -2
@@ 56,8 56,9 @@ type OS22X struct {

var _ Header = OS22X{}

func (c OS22X) VersionName() string { return "OS22X" }
func (c OS22X) Hint() Hint          { return OS2 }
func (c OS22X) VersionName() string    { return "OS22X" }
func (c OS22X) Hint() Hint             { return OS2 }
func (c OS22X) BitmapDIBSize() DIBSize { return c.DIBSize }

func (c OS22X) Regions(fh FileHeader) (r Regions) {
	r.PixData.Start = int64(fh.PixOffset)

M os2core.go => os2core.go +3 -2
@@ 16,8 16,9 @@ type OS2Core struct {

var _ Header = OS2Core{}

func (c OS2Core) VersionName() string { return "OS2Core" }
func (c OS2Core) Hint() Hint          { return OS2 }
func (c OS2Core) VersionName() string    { return "OS2Core" }
func (c OS2Core) Hint() Hint             { return OS2 }
func (c OS2Core) BitmapDIBSize() DIBSize { return c.DIBSize }

func (c OS2Core) Regions(fh FileHeader) (r Regions) {
	r.PixData.Start = int64(fh.PixOffset)

M read.go => read.go +35 -2
@@ 32,7 32,7 @@ type Bitmap struct {
	PixDataRangeValid    bool
}

func DecodeBitmapHeader(data []byte, hint Hint) (n int, bmp BitmapHeader, err error) {
func DecodeBitmapFileHeader(data []byte, hint Hint) (n int, bmp BitmapHeader, err error) {
	rdr := bytes.NewReader(data)
	pos := func() int {
		p, _ := rdr.Seek(0, io.SeekCurrent)


@@ 78,8 78,41 @@ func DecodeBitmapHeader(data []byte, hint Hint) (n int, bmp BitmapHeader, err er
	return 0, bmp, nil
}

func DecodeBitmapDIBHeader(data []byte, hint Hint) (n int, bmp Header, err error) {
	rdr := bytes.NewReader(data)
	pos := func() int {
		p, _ := rdr.Seek(0, io.SeekCurrent)
		return int(p)
	}
	defer func() {
		n = int(pos())
	}()

	var dibSize DIBSize
	var mark = pos()
	if err := binary.Read(rdr, encoding, &dibSize); err != nil {
		return 0, bmp, err
	}
	rdr.Seek(int64(mark), io.SeekStart)

	hdr, err := headerFromSize(dibSize, hint)
	if err != nil {
		return 0, bmp, err
	}

	if err := binary.Read(rdr, encoding, hdr); err == io.ErrUnexpectedEOF {
		if pos() != int(dibSize) {
			return 0, bmp, err
		}
	} else if err != nil {
		return 0, bmp, err
	}

	return 0, hdr, nil
}

func DecodeBitmap(data []byte, hint Hint) (n int, bmp *Bitmap, err error) {
	n, bh, err := DecodeBitmapHeader(data, hint)
	n, bh, err := DecodeBitmapFileHeader(data, hint)
	bmp = &Bitmap{BitmapHeader: bh}
	if err != nil {
		return n, bmp, nil