From 42bf437f0766b4fc549cdad95cbfa3a8beaa5aa4 Mon Sep 17 00:00:00 2001 From: Brian Evans Date: Thu, 18 Jul 2019 16:49:04 -0700 Subject: [PATCH] Its alivegit status --- .gitignore | 6 ++ imageHandler.go | 58 +++++++++++++ main.go | 219 ++++++++++++++++++++++++++++++++++++++++++++---- receivers.go | 20 ++++- test.frs | 22 ++--- 5 files changed, 294 insertions(+), 31 deletions(-) create mode 100644 imageHandler.go diff --git a/.gitignore b/.gitignore index 55edffc..6c49217 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,9 @@ # Binary from `go build` filtress + +# Test images +*.jpeg +*.jpg +*.png + diff --git a/imageHandler.go b/imageHandler.go new file mode 100644 index 0000000..36f245a --- /dev/null +++ b/imageHandler.go @@ -0,0 +1,58 @@ +package main + +import ( + "math" + "image" + "image/draw" + "image/jpeg" + "image/png" + "path/filepath" + "strings" + "fmt" + "os" +) + +func avgColor(orig, upd int) int { + a := float64(orig * orig) + b := float64(upd * upd) + return int(math.Sqrt(float64((a + b)/2))) +} + +func loadImage(path string) (*image.RGBA, string) { + f, err := os.Open(path) + defer f.Close() + if err != nil { + panic(fmt.Sprintf("File access error: Unable to open image at path %q", path)) + } + img, format, err := image.Decode(f) + rect := img.Bounds() + rgba := image.NewRGBA(rect) + draw.Draw(rgba, rect, img, rect.Min, draw.Src) + + return rgba, format +} + +func saveImage(iPath, filterName, encodeType string, img *image.RGBA) { + ext := filepath.Ext(iPath) + name := strings.TrimSuffix(filepath.Base(iPath), ext) + newImagePath := fmt.Sprintf("%s/%s_%s%s", filepath.Dir(iPath), name, filterName, ext) + fg, err := os.Create(newImagePath) + defer fg.Close() + if err != nil { + panic(fmt.Sprintf("File access error: Unable to save image to path %q", newImagePath)) + } + + if encodeType == "jpeg" { + err = jpeg.Encode(fg, img, nil) + if err != nil { + panic("Encode error: Unable to encode output as jpeg") + } + } else if encodeType == "png" { + err = png.Encode(fg, img) + if err != nil { + panic("Encode error: Unable to encode output as png") + } + } else { + panic(fmt.Sprintf("Encode error: Unable to encode output to unsupported format %q", encodeType)) + } +} diff --git a/main.go b/main.go index e1da090..33300f6 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,8 @@ import ( "flag" "fmt" "os" + "image" + "image/color" "path/filepath" "strings" "tildegit.org/sloum/filtress/parser" @@ -30,8 +32,11 @@ type filterState struct { 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) @@ -42,8 +47,192 @@ func openFileFromPath(path string) *os.File { 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() { - // Handle checking filter file argument + 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 { @@ -57,20 +246,18 @@ func main() { panic(fmt.Sprintf("Input error: Incorrect filetype. Expected .FRS, received \".%s\"", fext)) } - // Open file and parse - fmt.Println("Filtress v0.2.0") - filterFile := openFileFromPath(fpath) - parser := parser.NewParser(filterFile) - fmt.Printf("Parsing file: %s ...\n", fname) - tree := parser.Parse() - if len(tree.Loops) > 0 { - fmt.Printf("File has %d procedures\n", len(tree.Loops)) - fmt.Println("File is valid") - } else { - fmt.Println("Invalid input file") - } + if *validateFlag { + fmt.Printf("Validating file: %s ...\n", fname) + validateInput(fpath) + } - fmt.Print(tree) - fmt.Println() + 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) } diff --git a/receivers.go b/receivers.go index 71819c4..78c6e89 100644 --- a/receivers.go +++ b/receivers.go @@ -5,10 +5,10 @@ import ( "tildegit.org/sloum/filtress/parser" ) -func (f *filter) update(change parser.Expression, vars map[string]int) { +func (f *filter) update(change parser.Expression) { opand := change.Opperand if change.Variable != "" { - if val, ok := vars[change.Variable]; ok { + if val, ok := variables[change.Variable]; ok { opand = val } else { panic(fmt.Sprintf("Runtime error: Encountered unknown variable %q", val)) @@ -28,6 +28,12 @@ func (f *filter) update(change parser.Expression, vars map[string]int) { } else { f.val /= opand } + case '%': + if opand == 0 { + f.val = 1 + } else { + f.val %= opand + } case '=': f.val = opand } @@ -39,7 +45,7 @@ func (f *filter) update(change parser.Expression, vars map[string]int) { } } -func (p *point) update(change []parser.Expression, vars map[string]int) { +func (p *point) update(change []parser.Expression) { var totalX, totalY, val, opand, max int var target *int @@ -57,7 +63,7 @@ func (p *point) update(change []parser.Expression, vars map[string]int) { } if e.Variable != "" { - if val, ok := vars[e.Variable]; ok { + if val, ok := variables[e.Variable]; ok { opand = val } else { panic(fmt.Sprintf("Runtime error: Encountered unknown variable %q", val)) @@ -79,6 +85,12 @@ func (p *point) update(change []parser.Expression, vars map[string]int) { } else { *target = val / opand } + case '%': + if opand == 0 { + *target = 1 + } else { + *target = val % opand + } case '=': *target = opand } diff --git a/test.frs b/test.frs index 176fdf9..e3b872f 100644 --- a/test.frs +++ b/test.frs @@ -1,17 +1,17 @@ # First attempt at a filtress file!! MOD 1 +APH 255 RED 100 - -BEG 10 -LOC +100:WID -BLU /300 +BLU 100 +GRN 100 +# This loop should create a bunch of vertical lines +BEG 50 + RED +23 + BLU -81 + GRN *27 + APH -5 + APY +5:HIG + LOC +20:+0 END -#comment -APY +100:+100 - - - - -