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 col struct { red filter green filter blue filter alpha filter } 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.Procedures) > 0 { fmt.Printf("File has %d procedures\n", len(tree.Procedures)) 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... ") im, imFormat = loadImage(path) imSize := im.Bounds().Size() variables["RG1"] = 0 variables["RG2"] = 0 fil.location.maxX = imSize.X fil.location.maxY = imSize.Y fil = filterState{ filter{255, 0, 255}, filter{255, 0,255}, filter{255, 0,255}, filter{255, 0,255}, point{0,0,imSize.X,imSize.Y}, filter{3,0,0}, } fmt.Print("Filtering image... ") for _, proc := range tree.Procedures { procedure(proc) } fmt.Print("Saving image... \n") saveImage(path, fname, imFormat, im) fmt.Print("Image saved. Done. \n") } func updateRegister(reg string, exp parser.Expression) { var err error opand := exp.Opperand if exp.Variable != "" { opand, err = retrieveVariableValue(exp.Variable) if err != nil { panic(fmt.Sprintf("Runtime error: Unknown variable %q encountered", 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 retrieveVariableValue(ident string) (int, error) { var val int switch ident { case "RG1", "RG2": val = variables[ident] case "WID": val = fil.location.maxX case "HIG": val = fil.location.maxY case "RED": val = fil.red.val case "GRN": val = fil.green.val case "BLU": val = fil.blue.val case "APH": val = fil.alpha.val case "LOX": val = fil.location.x case "LOY": val = fil.location.y default: return 0, fmt.Errorf("Unknown variable %q sent to retrieveVariableValue", ident) } return val, nil } func procedure(p parser.Procedure) { switch p.Kind { case "LOC": if len(p.Expressions) == 2 { fil.location.update(p.Expressions) } case "LOX": if len(p.Expressions) == 1 { y := parser.Expression{Opperator: '+', Opperand: 0, Variable: ""} p.Expressions = append(p.Expressions, y) fil.location.update(p.Expressions) } case "LOY": if len(p.Expressions) == 1 { x := parser.Expression{Opperator: '+', Opperand: 0, Variable: ""} exps := make([]parser.Expression,0,2) exps = append(exps, x, p.Expressions[0]) fil.location.update(exps) } case "MOD": if len(p.Expressions) >= 1 { fil.mode.update(p.Expressions[0]) } case "RG1", "RG2": if len(p.Expressions) >= 1 { updateRegister(p.Kind, 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]) } case "GET": fil.getColors() } } func applyToImage(exps []parser.Expression) { var r,g,b,a int var col color.RGBA var err error if len(exps) == 0 { r = fil.red.val g = fil.green.val b = fil.blue.val a = fil.alpha.val if fil.mode.val != 0 { col = im.RGBAAt(fil.location.x, fil.location.y) r = avgColor(int(col.R), r) g = avgColor(int(col.G), g) b = avgColor(int(col.B), b) a = avgColor(int(col.A), a) } im.SetRGBA( fil.location.x, fil.location.y, color.RGBA{ uint8(r), uint8(g), uint8(b), uint8(a), }) } else if len(exps) == 2 { endX := exps[0].Opperand endY := exps[1].Opperand if exps[0].Variable != "" { endX, err = retrieveVariableValue(exps[0].Variable) if err != nil { panic(fmt.Sprintf("Runtime error: Unknown variable %q encountered", exps[0].Variable)) } } if exps[1].Variable != "" { endY, err = retrieveVariableValue(exps[1].Variable) if err != nil { panic(fmt.Sprintf("Runtime error: Unknown variable %q encountered", exps[1].Variable)) } } endX = getBoxBound(fil.location.x, endX, fil.location.maxX, exps[0].Opperator) endY = getBoxBound(fil.location.y, endY, fil.location.maxY, exps[1].Opperator) for y := fil.location.y; y <= endY; y++ { for x := fil.location.x; x <= endX; x++ { r = fil.red.val g = fil.green.val b = fil.blue.val a = fil.alpha.val if fil.mode.val != 0 { col = im.RGBAAt(x, y) r = avgColor(int(col.R), r) g = avgColor(int(col.G), g) b = avgColor(int(col.B), b) a = avgColor(int(col.A), a) } im.SetRGBA( x, y, color.RGBA{ uint8(r), uint8(g), uint8(b), uint8(a), }) } } } } 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) }