mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Create tool for converting images to compatible console logo files
The tool processes an image and converts it to a logo.Image struct which can be assigned to a logo-capable console.
This commit is contained in:
parent
99977294aa
commit
a5c6828fc2
44
src/gopheros/device/video/console/logo/logo.go
Normal file
44
src/gopheros/device/video/console/logo/logo.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Package logo contains logos that can be used with a framebuffer console.
|
||||
package logo
|
||||
|
||||
import "image/color"
|
||||
|
||||
// ConsoleLogo defines the logo used by framebuffer consoles. If set to nil
|
||||
// then no logo will be displayed.
|
||||
var ConsoleLogo *Image
|
||||
|
||||
// Alignment defines the supported horizontal alignments for a console logo.
|
||||
type Alignment uint8
|
||||
|
||||
const (
|
||||
// AlignLeft aligns the logo to the left side of the console.
|
||||
AlignLeft Alignment = iota
|
||||
|
||||
// AlignCenter aligns the logo to the center of the console.
|
||||
AlignCenter
|
||||
|
||||
// AlignRight aligns the logo to the right side of the console.
|
||||
AlignRight
|
||||
)
|
||||
|
||||
// Image describes an 8bpp image with
|
||||
type Image struct {
|
||||
// The width and height of the logo in pixels.
|
||||
Width uint32
|
||||
Height uint32
|
||||
|
||||
// Align specifies the horizontal alignment for the logo.
|
||||
Align Alignment
|
||||
|
||||
// TransparentIndex defines a color index that will be treated as
|
||||
// transparent when drawing the logo.
|
||||
TransparentIndex uint8
|
||||
|
||||
// The palette for the logo. The console remaps the palette
|
||||
// entries to the end of its own palette.
|
||||
Palette []color.RGBA
|
||||
|
||||
// The logo data comprises of Width*Height bytes where each byte
|
||||
// represents an index in the logo palette.
|
||||
Data []uint8
|
||||
}
|
195
tools/makelogo/makelogo.go
Normal file
195
tools/makelogo/makelogo.go
Normal file
@ -0,0 +1,195 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"image"
|
||||
"image/color"
|
||||
"os"
|
||||
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
// The max number of colors that are allowed in a logo.
|
||||
const maxColors = 16
|
||||
|
||||
func exit(err error) {
|
||||
fmt.Fprintf(os.Stderr, "[makelogo] error: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func buildPalette(img image.Image, transColor color.RGBA) ([]color.RGBA, map[color.RGBA]int, error) {
|
||||
var (
|
||||
palette []color.RGBA
|
||||
colorToPalIndex = make(map[color.RGBA]int)
|
||||
)
|
||||
|
||||
// Transparent color is always first
|
||||
palette = append(palette, transColor)
|
||||
colorToPalIndex[palette[0]] = 0
|
||||
|
||||
bounds := img.Bounds()
|
||||
for y := 0; y < bounds.Size().Y; y++ {
|
||||
for x := 0; x < bounds.Size().X; x++ {
|
||||
r, g, b, _ := img.At(x, y).RGBA()
|
||||
c := color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b)}
|
||||
if _, exists := colorToPalIndex[c]; exists {
|
||||
continue
|
||||
}
|
||||
|
||||
colorToPalIndex[c] = len(colorToPalIndex)
|
||||
palette = append(palette, c)
|
||||
}
|
||||
}
|
||||
|
||||
if got := len(palette); got > maxColors {
|
||||
return nil, nil, fmt.Errorf("logo should not contain more than %d colors; got %d", maxColors, got)
|
||||
}
|
||||
|
||||
return palette, colorToPalIndex, nil
|
||||
}
|
||||
|
||||
func genLogoFile(img image.Image, transColor color.RGBA, logoVar, align string) (string, error) {
|
||||
var (
|
||||
buf bytes.Buffer
|
||||
bounds = img.Bounds()
|
||||
logoVarName = fmt.Sprintf("%s%dx%d", logoVar, bounds.Size().X, bounds.Size().Y)
|
||||
)
|
||||
|
||||
// Generate palette
|
||||
palette, colorToPalIndex, err := buildPalette(img, transColor)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Output header
|
||||
fmt.Fprintf(&buf, `
|
||||
package logo
|
||||
|
||||
import "image/color"
|
||||
|
||||
var (
|
||||
%s = Image{
|
||||
Width: %d,
|
||||
Height: %d,
|
||||
Align: %s,
|
||||
TransparentIndex: 0,
|
||||
`, logoVarName, bounds.Size().X, bounds.Size().Y, align)
|
||||
|
||||
// Output palette
|
||||
fmt.Fprint(&buf, "Palette: []color.RGBA{\n")
|
||||
for _, c := range palette {
|
||||
fmt.Fprintf(&buf, "\t{R:%d, G:%d, B:%d},\n", c.R, c.G, c.B)
|
||||
}
|
||||
fmt.Fprint(&buf, "},\n")
|
||||
|
||||
// Output image data
|
||||
fmt.Fprint(&buf, "Data: []uint8{\n")
|
||||
|
||||
pixelIndex := 0
|
||||
for y := 0; y < bounds.Size().Y; y++ {
|
||||
for x := 0; x < bounds.Size().X; x, pixelIndex = x+1, pixelIndex+1 {
|
||||
if pixelIndex != 0 && pixelIndex%16 == 0 {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
|
||||
r, g, b, _ := img.At(x, y).RGBA()
|
||||
colorIndex := colorToPalIndex[color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b)}]
|
||||
|
||||
fmt.Fprintf(&buf, "0x%x, ", colorIndex)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(&buf, "\n},\n")
|
||||
|
||||
// Footer
|
||||
fmt.Fprint(&buf, "}\n)\n")
|
||||
fmt.Fprintf(&buf, "func init(){\navailableLogos = append(availableLogos, &%s)\n}\n", logoVarName)
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func runTool() error {
|
||||
transR := flag.Uint("trans-r", 255, "the red component value for the transparent color")
|
||||
transG := flag.Uint("trans-g", 0, "the green component value for the transparent color")
|
||||
transB := flag.Uint("trans-b", 255, "the blue component value for the transparent color")
|
||||
logoVar := flag.String("var-name", "logo", "the name of the variable containing the logo data")
|
||||
align := flag.String("align", "center", "the horizontal alignment for the logo (left, center or right)")
|
||||
output := flag.String("out", "-", "a file to write the generated logo or - to output to STDOUT")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprint(os.Stderr, "makelogo: convert a png/jpg or gif image to a 8bpp console logo\n\n")
|
||||
fmt.Fprint(os.Stderr, "Usage: makelogo [options] image\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
exit(errors.New("missing image file argument"))
|
||||
}
|
||||
|
||||
switch *align {
|
||||
case "left":
|
||||
*align = "AlignLeft"
|
||||
case "center":
|
||||
*align = "AlignCenter"
|
||||
case "right":
|
||||
*align = "AlignRight"
|
||||
default:
|
||||
exit(errors.New("invalid alignment specification; supported values are: left, center or right"))
|
||||
}
|
||||
|
||||
f, err := os.Open(flag.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logoData, err := genLogoFile(
|
||||
img,
|
||||
color.RGBA{R: uint8(*transR), G: uint8(*transG), B: uint8(*transB)},
|
||||
*logoVar,
|
||||
*align,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Pretty-print generated file using go/printer
|
||||
fSet := token.NewFileSet()
|
||||
astFile, err := parser.ParseFile(fSet, "", logoData, parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch *output {
|
||||
case "-":
|
||||
printer.Fprint(os.Stdout, fSet, astFile)
|
||||
default:
|
||||
fOut, err := os.Create(*output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fOut.Close()
|
||||
|
||||
printer.Fprint(fOut, fSet, astFile)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := runTool(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user