forked from fiam/max7456tool
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial public commit for max7456tool
- Loading branch information
0 parents
commit f2052ff
Showing
10 changed files
with
731 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/max7456tool |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"image" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
|
||
"gopkg.in/urfave/cli.v1" | ||
) | ||
|
||
func buildAction(ctx *cli.Context) error { | ||
if ctx.NArg() != 2 { | ||
return errors.New("build requires 2 arguments, see help build") | ||
} | ||
dir := ctx.Args().Get(0) | ||
entries, err := ioutil.ReadDir(dir) | ||
if err != nil { | ||
return err | ||
} | ||
chars := make(map[int]*MCMChar) | ||
var builder charBuilder | ||
for _, e := range entries { | ||
if e.IsDir() { | ||
continue | ||
} | ||
name := e.Name() | ||
ext := filepath.Ext(name) | ||
if strings.ToLower(ext) == ".png" { | ||
nonExt := name[:len(name)-len(ext)] | ||
// Parse the name. It might contain multiple images | ||
lines := strings.Split(nonExt, "_") | ||
var nums [][]int | ||
w := 0 | ||
h := len(lines) | ||
for _, line := range lines { | ||
var lineNums []int | ||
items := strings.Split(line, "-") | ||
if w != 0 && w != len(items) { | ||
return fmt.Errorf("uneven lines in filename %q: %d vs %d", nonExt, w, len(items)) | ||
} | ||
w = len(items) | ||
for _, v := range items { | ||
n, err := strconv.Atoi(v) | ||
if err != nil { | ||
return fmt.Errorf("invalid number %q if image filename %q: %v", v, nonExt, err) | ||
} | ||
lineNums = append(lineNums, n) | ||
} | ||
nums = append(nums, lineNums) | ||
} | ||
// Decode the image | ||
filename := filepath.Join(dir, name) | ||
imf, err := os.Open(filename) | ||
if err != nil { | ||
return err | ||
} | ||
im, imfmt, err := image.Decode(imf) | ||
if err != nil { | ||
return fmt.Errorf("error decoding %s: %v", filename, err) | ||
} | ||
if err := imf.Close(); err != nil { | ||
return err | ||
} | ||
if imfmt != "png" { | ||
return fmt.Errorf("%s: invalid image format %s, must be png", filename, imfmt) | ||
} | ||
if im.Bounds().Dx() != w*charWidth { | ||
return fmt.Errorf("image with %d characters per line must have a width of %d, not %d", w, w*charWidth, im.Bounds().Dx()) | ||
} | ||
if im.Bounds().Dy() != h*charHeight { | ||
return fmt.Errorf("image with %d characters per column must have a height of %d, not %d", h, h*charHeight, im.Bounds().Dy()) | ||
} | ||
bounds := im.Bounds() | ||
// Import each character | ||
for jj := 0; jj < h; jj++ { | ||
for ii := 0; ii < w; ii++ { | ||
chNum := nums[jj][ii] | ||
x0 := bounds.Min.X + ii*charWidth | ||
y0 := bounds.Min.Y + jj*charHeight | ||
if debugFlag { | ||
log.Printf("importing char %d from image %v @%d,%d", chNum, name, x0, y0) | ||
} | ||
builder.Reset() | ||
for y := y0; y < y0+charHeight; y++ { | ||
for x := x0; x < x0+charWidth; x++ { | ||
r, g, b, a := im.At(x, y).RGBA() | ||
var p MCMPixel | ||
switch { | ||
case r == 0 && g == 0 && b == 0 && a == 65535: | ||
p = MCMPixelBlack | ||
case r == 65535 && g == 65535 && b == 65535 && a == 65535: | ||
p = MCMPixelWhite | ||
default: | ||
p = MCMPixelTransparent | ||
} | ||
builder.AppendPixel(p) | ||
} | ||
} | ||
for !builder.IsComplete() { | ||
builder.AppendPixel(MCMPixelTransparent) | ||
} | ||
if _, found := chars[chNum]; found { | ||
return fmt.Errorf("duplicate character %d", chNum) | ||
} | ||
chars[chNum] = builder.Char() | ||
builder.Reset() | ||
} | ||
} | ||
} | ||
} | ||
output := ctx.Args().Get(1) | ||
f, err := openOutputFile(output) | ||
if err != nil { | ||
return err | ||
} | ||
enc := &Encoder{ | ||
Chars: chars, | ||
Fill: !ctx.Bool("no-blanks"), | ||
} | ||
if err := enc.Encode(f); err != nil { | ||
// Remove the file, since it can't be | ||
// a proper .mcm at this point | ||
os.Remove(output) | ||
return err | ||
} | ||
if err := f.Close(); err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"image" | ||
"image/color" | ||
) | ||
|
||
const ( | ||
charWidth = 12 | ||
charHeight = 18 | ||
|
||
// See type MCMChar | ||
minCharBytes = 54 | ||
charBytes = 64 | ||
) | ||
|
||
var ( | ||
blankCharacter = constantChar(85) // all pixels = 01 | ||
) | ||
|
||
// MCMPixel represents a pixel in a character. Each pixel must be one | ||
// of MCMPixelBlack, MCMPixelTransparent or MCMPixelWhite. | ||
type MCMPixel byte | ||
|
||
const ( | ||
// MCMPixelBlack represents a black pixel | ||
MCMPixelBlack MCMPixel = 0 | ||
// MCMPixelTransparent represents a transparent/gray pixel, | ||
// depending on the OSD mode. | ||
MCMPixelTransparent = 1 | ||
// MCMPixelWhite represents a white pixel | ||
MCMPixelWhite = 2 | ||
) | ||
|
||
func (p MCMPixel) isTransparent() bool { | ||
// Transparent pixels have the LSB | ||
// set to 1 while MSB is ignored. | ||
return (p & MCMPixelTransparent) == MCMPixelTransparent | ||
} | ||
|
||
// MCMChar represents a character in the character map. | ||
// Each character has 12x18 lines, where each pixel is represented | ||
// by 2 bits. Thus, each character requires ((12*18)*2)/8 = 54 bytes. | ||
// However, MCM files use 64 bytes per character ignoring the rest | ||
// (according to Maxim, to make adressing easier). | ||
type MCMChar struct { | ||
data []byte | ||
} | ||
|
||
// Data returns a copy of the raw pixel data. | ||
func (c *MCMChar) Data() []byte { | ||
data := make([]byte, len(c.data)) | ||
copy(data, c.data) | ||
return data | ||
} | ||
|
||
// ForEachPixel calls f for each pixel in the character. | ||
// 0 <= x <= 12 while y >= 0. Note that a character might | ||
// have extra ignored pixels at the end. unused will be true | ||
// for those ones. p will always be one | ||
// of the constants defined for MCMPixel | ||
func (c *MCMChar) ForEachPixel(f func(x, y int, unused bool, p MCMPixel)) { | ||
x := 0 | ||
y := 0 | ||
for _, v := range c.data { | ||
unused := y >= charHeight | ||
f(x, y, unused, MCMPixel((0xC0&v)>>6)) | ||
f(x+1, y, unused, MCMPixel((0x30&v)>>4)) | ||
f(x+2, y, unused, MCMPixel((0xC&v)>>2)) | ||
f(x+3, y, unused, MCMPixel(0x03&v)) | ||
|
||
x += 4 | ||
if x == charWidth { | ||
x = 0 | ||
y++ | ||
} | ||
} | ||
} | ||
|
||
// Image returns a 12x18 image of the character | ||
func (c *MCMChar) Image(transparent color.Color) image.Image { | ||
if isNilColor(transparent) { | ||
transparent = defaultTransparentColor | ||
} | ||
im := image.NewRGBA(image.Rect(0, 0, charWidth, charHeight)) | ||
c.ForEachPixel(func(x, y int, unused bool, p MCMPixel) { | ||
if unused { | ||
return | ||
} | ||
var c color.Color | ||
switch p { | ||
case MCMPixelTransparent: | ||
c = transparent | ||
case MCMPixelBlack: | ||
c = blackColor | ||
case MCMPixelWhite: | ||
c = whiteColor | ||
default: | ||
// Should not happen | ||
panic(fmt.Errorf("invalid pixel %v", p)) | ||
} | ||
im.Set(x, y, c) | ||
}) | ||
return im | ||
} | ||
|
||
func (c *MCMChar) isBlank() bool { | ||
blank := true | ||
c.ForEachPixel(func(x, y int, unused bool, p MCMPixel) { | ||
if p != MCMPixelTransparent { | ||
blank = false | ||
} | ||
}) | ||
return blank | ||
} | ||
|
||
func constantChar(b byte) *MCMChar { | ||
data := make([]byte, charBytes) | ||
for ii := range data { | ||
data[ii] = b | ||
} | ||
return &MCMChar{data: data} | ||
} | ||
|
||
type charBuilder struct { | ||
c *MCMChar | ||
pixel int | ||
} | ||
|
||
func (b *charBuilder) Char() *MCMChar { | ||
return b.c | ||
} | ||
|
||
func (b *charBuilder) Reset() { | ||
b.c = new(MCMChar) | ||
b.pixel = 0 | ||
} | ||
|
||
func (b *charBuilder) IsEmpty() bool { | ||
return len(b.c.data) == 0 | ||
} | ||
|
||
func (b *charBuilder) IsComplete() bool { | ||
return len(b.c.data) == charBytes && b.pixel == 0 | ||
} | ||
|
||
func (b *charBuilder) AppendPixel(p MCMPixel) error { | ||
if p.isTransparent() { | ||
p = MCMPixelTransparent | ||
} | ||
if p > 3 { | ||
return fmt.Errorf("invalid pixel %v > 3", p) | ||
} | ||
pb := byte(p) | ||
switch b.pixel { | ||
case 0: | ||
// Append new byte | ||
b.c.data = append(b.c.data, pb<<6) | ||
case 1: | ||
b.c.data[len(b.c.data)-1] |= pb << 4 | ||
case 2: | ||
b.c.data[len(b.c.data)-1] |= pb << 2 | ||
case 3: | ||
b.c.data[len(b.c.data)-1] |= pb | ||
} | ||
b.pixel++ | ||
if b.pixel == 4 { | ||
b.pixel = 0 | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package main | ||
|
||
import ( | ||
"image/color" | ||
"reflect" | ||
) | ||
|
||
var ( | ||
blackColor = &color.RGBA{R: 0, G: 0, B: 0, A: 255} | ||
whiteColor = &color.RGBA{R: 255, G: 255, B: 255, A: 255} | ||
defaultTransparentColor = &color.RGBA{R: 128, G: 128, B: 128, A: 255} // 50% gray | ||
) | ||
|
||
func isNilColor(c color.Color) bool { | ||
if c == nil { | ||
return true | ||
} | ||
v := reflect.ValueOf(c) | ||
return v.Kind() == reflect.Ptr && v.IsNil() | ||
} |
Oops, something went wrong.