/* SPDX-License-Identifier: MIT */ package main import ( "bufio" "encoding/hex" "fmt" "github.com/spf13/pflag" "github.com/wayneashleyberry/truecolor/pkg/color" "math" "os" ) var output *os.File var ( outputFile string verbose bool version bool colors bool print bool help bool ) func init() { pflag.StringVarP( &outputFile, "output", "o", "out.hex", "output file", ) pflag.BoolVarP( &print, "print", "p", false, "print to stdout instead of output file. -o will have no effect", ) pflag.BoolVarP( &colors, "colors", "c", false, "print colors to terminal (needs 24 bit color support)", ) pflag.BoolVarP( &verbose, "verbose", "v", false, "verbose output", ) pflag.BoolVar( &version, "version", false, "version info", ) pflag.BoolVarP( &help, "help", "h", false, "displays help", ) } // converts different size bytes to float64 to compare // high and low and return the one thats closer func compareChannels(base, high, low byte) byte { absHigh := math.Abs(float64(high - base)) absLow := math.Abs(float64(low - base)) if absHigh < absLow { return high } return low } func printDefaults() { fmt.Fprintf(os.Stderr, "Usage: %s [options] [-o outfile] infile\n", os.Args[0]) fmt.Fprintf(os.Stderr, "Options:\n") pflag.PrintDefaults() } func main() { pflag.Usage = printDefaults pflag.Parse() if version { fmt.Fprintf(os.Stderr, "%s is a utility that converts RGB24 hex palette files to 8bit 3-3-2 RGB.\n", os.Args[0]) fmt.Println("rgb2rgb8 v0.1.0") fmt.Println("(c) 2025 Amber Zeller") fmt.Println("Distributed under the MIT license") os.Exit(0) } if help { printDefaults() os.Exit(0) } if len(pflag.Args()) == 0 { fmt.Println("Error: No file specified.") printDefaults() os.Exit(1) } var inputFile = pflag.Args()[0] if verbose { fmt.Println("outputFile:", outputFile) fmt.Println("inputFile:", inputFile) } //read input file input, err := os.Open(inputFile) if err != nil { fmt.Println("Error: Cannot read input file", err) os.Exit(2) } defer input.Close() //create output file if !print { output, err = os.Create(outputFile) if err != nil { fmt.Println("Error: Cannot create output file.", err) os.Exit(3) } } //scan through input scanner := bufio.NewScanner(input) var data string counter := 1 for scanner.Scan() { line := scanner.Text() if verbose { fmt.Println() fmt.Println("Parsing line:", line) } lineHex, err := hex.DecodeString(line) if err != nil || len(lineHex) != 3 { fmt.Println("Error: Failed to parse line as hex.", err) } else { //separate rgb components red := byte(lineHex[0]) green := byte(lineHex[1]) blue := byte(lineHex[2]) //and with 11100000 red3bitLow := red & 224 green3bitLow := green & 224 //and with 11000000 blue2bitLow := blue & 192 //and with 11100000 and add 00011111 red3bitHigh := (red & 224) + 31 green3bitHigh := (green & 224) + 31 //and with 11000000 and add 00111111 blue2bitHigh := (blue & 192) + 63 //combine color channels into bytes again rgb8High := []byte{red3bitHigh, green3bitHigh, blue2bitHigh} rgb8Low := []byte{red3bitLow, green3bitLow, blue2bitLow} //compare channels to find closer one to original redFinal := compareChannels(red, red3bitHigh, red3bitLow) greenFinal := compareChannels(green, green3bitHigh, green3bitLow) blueFinal := compareChannels(blue, blue2bitHigh, blue2bitLow) //combine final color channels rgb8Final := []byte{redFinal, greenFinal, blueFinal} if verbose { fmt.Println("Raw RGB values:", red, green, blue) fmt.Println("High values:", red3bitHigh, green3bitHigh, blue2bitHigh) fmt.Println("Low values:", red3bitLow, green3bitLow, blue2bitLow) fmt.Println("Final values:", redFinal, greenFinal, blueFinal) fmt.Println("Hex high value:", hex.EncodeToString(rgb8High)) fmt.Println("Hex high value:", hex.EncodeToString(rgb8Low)) fmt.Println("Hex final value:", hex.EncodeToString(rgb8Final)) } //generate pretty color patterns if colors { fmt.Println("Raw High Low Final") color.Background(red, green, blue).Print(" ") color.Background(red3bitHigh, green3bitHigh, blue2bitHigh).Print(" ") color.Background(red3bitLow, green3bitLow, blue2bitLow).Print(" ") color.Background(redFinal, greenFinal, blueFinal).Println(" ") } data += hex.EncodeToString(rgb8Final) + "\n" } counter++ } if scanner.Err() != nil { fmt.Println(scanner.Err()) } if print { fmt.Fprintf(os.Stderr, "%s", data) } else { _, err = output.WriteString(data) if err != nil { fmt.Println("Error: Failed to write output file.", err) os.Exit(4) } output.Close() } }