package main import ( "flag" "fmt" "os" "image" "image/color" "path/filepath" "strings" "tildegit.org/sloum/filtress/parser" ) type point struct { x int y int maxX int maxY int } type filter struct { max int min int val int } type filterState struct { red filter green filter blue filter alpha filter location point mode filter } var variables = map[string]int{} var tree parser.AST var fil filterState var im *image.RGBA var imFormat string func openFileFromPath(path string) *os.File { file, err := os.Open(path) if err != nil { panic(err) } return file } func generateAST(path string) { filterFile := openFileFromPath(path) defer filterFile.Close() parser := parser.NewParser(filterFile) tree = parser.Parse() } func validateInput(path string) { generateAST(path) if len(tree.Loops) > 0 { fmt.Printf("File has %d procedures\n", len(tree.Loops)) fmt.Println("File is valid") } else { fmt.Println("Invalid or empty input file") } os.Exit(0) } func filterImage(path, fname string) { fmt.Print("Loading image \r") im, imFormat = loadImage(path) imSize := im.Bounds().Size() variables["WID"] = imSize.X variables["HIG"] = imSize.Y variables["REG"] = 0 fil = filterState{ filter{255, 255, 255}, filter{255,255,255}, filter{255,255,255}, filter{255,255,255}, point{0,0,imSize.X,imSize.Y}, filter{3,0,0}, } fmt.Print("Filtering image \r") for _, loop := range tree.Loops { for ;loop.Counter > 0; loop.Counter-- { for _, proc := range loop.Procedures { procedure(proc) } } } fmt.Print("Saving image \n") saveImage(path, fname, imFormat, im) } func updateRegister(exp parser.Expression) { opand := exp.Opperand if exp.Variable != "" { opand = variables[exp.Variable] } switch exp.Opperator { case '+': variables["REG"] += opand case '-': variables["REG"] -= opand case '*': variables["REG"] *= opand case '/': if opand == 0 { variables["REG"] = 1 } else { variables["REG"] /= opand } case '=': variables["REG"] = opand } } func procedure(p parser.Procedure) { switch p.Kind { case "LOC": if len(p.Expressions) == 2 { fil.location.update(p.Expressions) } case "MOD": if len(p.Expressions) >= 1 { fil.mode.update(p.Expressions[0]) } case "REG": if len(p.Expressions) >= 1 { updateRegister(p.Expressions[0]) } case "APY": applyToImage(p.Expressions) case "RED": if len(p.Expressions) >= 1 { fil.red.update(p.Expressions[0]) } case "BLU": if len(p.Expressions) >= 1 { fil.blue.update(p.Expressions[0]) } case "GRN": if len(p.Expressions) >= 1 { fil.green.update(p.Expressions[0]) } case "APH": if len(p.Expressions) >= 1 { fil.alpha.update(p.Expressions[0]) } case "COL": if len(p.Expressions) == 4 { fil.red.update(p.Expressions[0]) fil.green.update(p.Expressions[1]) fil.blue.update(p.Expressions[2]) fil.alpha.update(p.Expressions[3]) } } } func applyToImage(exps []parser.Expression) { if len(exps) == 0 { im.SetRGBA( fil.location.x, fil.location.y, color.RGBA{ uint8(fil.red.val), uint8(fil.green.val), uint8(fil.blue.val), uint8(fil.alpha.val), }) } else if len(exps) == 2 { endX := exps[0].Opperand endY := exps[1].Opperand if exps[0].Variable != "" { endX = variables[exps[0].Variable] } if exps[1].Variable != "" { endY = variables[exps[1].Variable] } endX = getBoxBound(fil.location.x, endX, variables["WID"], exps[0].Opperator) endY = getBoxBound(fil.location.y, endY, variables["HIG"], exps[0].Opperator) for y := fil.location.y; y <= endY; y++ { for x := fil.location.x; x <= endX; x++ { im.SetRGBA( x, y, color.RGBA{ uint8(fil.red.val), uint8(fil.green.val), uint8(fil.blue.val), uint8(fil.alpha.val), }) } } } } func getBoxBound(start, end, max int, op rune) int { var out int switch op { case '+': out = start + end case '-': out = start - end case '*': out = start * end case '/': if end == 0 { out = 0 } else { out = start / end } case '%': if end == 0 { out = 0 } else { out = start % end } case '=': out = start } if out > max { out = max } else if out < 0 { out = 0 } return out } func main() { validateFlag := flag.Bool("v", false, "If present, will validate the provided frs file and exit") inputFlag := flag.String("image", "", "Path to the image to be filtered") flag.Parse() posArgs := flag.Args() if len(posArgs) > 1 || len(posArgs) < 1 { panic(fmt.Sprintf("Input error: Too many positional arguments. Expected one, received %d", len(flag.Args()))) } fpath := posArgs[0] fext := strings.ToUpper(filepath.Ext(fpath)) _, fname := filepath.Split(fpath) if fext != ".FRS" { panic(fmt.Sprintf("Input error: Incorrect filetype. Expected .FRS, received \".%s\"", fext)) } if *validateFlag { fmt.Printf("Validating file: %s ...\n", fname) validateInput(fpath) } if *inputFlag == "" { fmt.Println("Input error: No image file was provided to filtress") os.Exit(1) } fmt.Printf("Parsing file: %s ...\n", fname) generateAST(fpath) frsName := strings.Split(fname, ".")[0] filterImage(*inputFlag, frsName) }