filtress/main.go

357 lines
7.5 KiB
Go
Raw Normal View History

package main
import (
"flag"
"fmt"
"os"
2019-07-18 23:49:04 +00:00
"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
sample col
}
var variables = map[string]int{}
2019-07-18 23:49:04 +00:00
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
}
2019-07-18 23:49:04 +00:00
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))
2019-07-18 23:49:04 +00:00
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... ")
2019-07-18 23:49:04 +00:00
im, imFormat = loadImage(path)
imSize := im.Bounds().Size()
variables["RG1"] = 0
variables["RG2"] = 0
2019-07-19 05:08:04 +00:00
fil.location.maxX = imSize.X
fil.location.maxY = imSize.Y
2019-07-18 23:49:04 +00:00
fil = filterState{
2019-07-19 05:08:04 +00:00
filter{255, 0, 255},
filter{255, 0,255},
filter{255, 0,255},
filter{255, 0,255},
2019-07-18 23:49:04 +00:00
point{0,0,imSize.X,imSize.Y},
filter{3,0,0},
col{
filter{255, 0, 255},
filter{255, 0, 255},
filter{255, 0, 255},
filter{255, 0, 255},
},
2019-07-18 23:49:04 +00:00
}
fmt.Print("Filtering image... ")
for _, proc := range tree.Procedures {
procedure(proc)
2019-07-18 23:49:04 +00:00
}
fmt.Print("Saving image... \n")
2019-07-18 23:49:04 +00:00
saveImage(path, fname, imFormat, im)
2019-07-19 05:08:04 +00:00
fmt.Print("Image saved. Done. \n")
2019-07-18 23:49:04 +00:00
}
func updateRegister(reg string, exp parser.Expression) {
var err error
2019-07-18 23:49:04 +00:00
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))
}
2019-07-18 23:49:04 +00:00
}
switch exp.Opperator {
case '+':
variables[reg] += opand
2019-07-18 23:49:04 +00:00
case '-':
variables[reg] -= opand
2019-07-18 23:49:04 +00:00
case '*':
variables[reg] *= opand
2019-07-18 23:49:04 +00:00
case '/':
if opand == 0 {
variables[reg] = 1
2019-07-18 23:49:04 +00:00
} else {
variables[reg] /= opand
2019-07-18 23:49:04 +00:00
}
case '=':
variables[reg] = opand
2019-07-18 23:49:04 +00:00
}
}
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
}
2019-07-18 23:49:04 +00:00
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 "RG1", "RG2":
2019-07-18 23:49:04 +00:00
if len(p.Expressions) >= 1 {
updateRegister(p.Kind, p.Expressions[0])
2019-07-18 23:49:04 +00:00
}
case "APY":
applyToImage(p.Expressions, false)
2019-07-18 23:49:04 +00:00
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.sample.update()
case "PUT":
applyToImage(p.Expressions, true)
2019-07-18 23:49:04 +00:00
}
}
func applyToImage(exps []parser.Expression, useSample bool) {
var err error
2019-07-18 23:49:04 +00:00
if len(exps) == 0 {
if useSample {
im.SetRGBA(
fil.location.x,
fil.location.y,
color.RGBA{
uint8(fil.sample.red.val),
uint8(fil.sample.green.val),
uint8(fil.sample.blue.val),
uint8(fil.sample.alpha.val),
})
} else {
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),
})
}
2019-07-18 23:49:04 +00:00
} 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))
}
2019-07-18 23:49:04 +00:00
}
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))
}
2019-07-18 23:49:04 +00:00
}
2019-07-19 05:08:04 +00:00
endX = getBoxBound(fil.location.x, endX, fil.location.maxX, exps[0].Opperator)
endY = getBoxBound(fil.location.y, endY, fil.location.maxY, exps[1].Opperator)
2019-07-19 05:08:04 +00:00
var r,g,b,a int
var col color.RGBA
2019-07-18 23:49:04 +00:00
for y := fil.location.y; y <= endY; y++ {
for x := fil.location.x; x <= endX; x++ {
if useSample {
r = fil.sample.red.val
g = fil.sample.green.val
b = fil.sample.blue.val
a = fil.sample.alpha.val
} else {
r = fil.red.val
g = fil.green.val
b = fil.blue.val
a = fil.alpha.val
}
2019-07-19 05:08:04 +00:00
if fil.mode.val != 0 {
col = im.RGBAAt(x, y)
// fmt.Printf("Orig: %d, New: %d, Avg: %d\n", col.R, r, avgColor(int(col.R), r))
r = avgColor(int(col.R), r)
g = avgColor(int(col.G), g)
b = avgColor(int(col.B), b)
a = avgColor(int(col.A), a)
}
2019-07-18 23:49:04 +00:00
im.SetRGBA(
x,
y,
color.RGBA{
2019-07-19 05:08:04 +00:00
uint8(r),
uint8(g),
uint8(b),
uint8(a),
2019-07-18 23:49:04 +00:00
})
}
}
}
}
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() {
2019-07-18 23:49:04 +00:00
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))
}
2019-07-18 23:49:04 +00:00
if *validateFlag {
fmt.Printf("Validating file: %s ...\n", fname)
validateInput(fpath)
}
2019-07-18 23:49:04 +00:00
if *inputFlag == "" {
fmt.Println("Input error: No image file was provided to filtress")
os.Exit(1)
}
2019-07-18 23:49:04 +00:00
fmt.Printf("Parsing file: %s ...\n", fname)
generateAST(fpath)
frsName := strings.Split(fname, ".")[0]
filterImage(*inputFlag, frsName)
}