From 33effda37b254241896af046f8a201dc9f091ebd Mon Sep 17 00:00:00 2001 From: Rewan_ Date: Sun, 7 Mar 2021 02:26:52 +0100 Subject: [PATCH] Path management, loggers, and true pixel size. Readability improvements and diverse optimizations --- README.md | 54 ++++++++++++++++++++++------- main/console.go | 80 +++++++++++++++++++++++++++++++++++++++--- main/image.go | 78 +++++++++++++++++++++++------------------ main/main.go | 45 ++++++++++++++++-------- main/path.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ main/saves.go | 72 ++++++++++++++------------------------ main/tools.go | 38 ++++++++++---------- 7 files changed, 332 insertions(+), 127 deletions(-) create mode 100644 main/path.go diff --git a/README.md b/README.md index 3ed47e6..6633b4e 100644 --- a/README.md +++ b/README.md @@ -27,32 +27,62 @@ $ sudo mv pixelize [your $GOBIN or $GOPATH/bin] Pixelizer is meant to be simple to use, error-free and intuitive. To be reminded of the usage, you can sure read this readme, but you can also open your terminal and use : ``` -$ pixelize [OR] pixelize help [OR] pixelize man +$ pixelize / $ pixelize man / $ pixelize help ``` This will print the manual. Here is how it works : +1. Pixelize an image (Basic syntax) : +``` +$ pixelize [imageName] [X] [Y] [Z] [...] +``` +* imageName = path and name of the image to pixelize. +* X = Width of each group of pixels to fuse (Ex : 5 will fuse every group of 5\*5 pixels from the image) +* Y = Number of results, with increasing X at each result (Ex : 5 3 will print 3 results while fusing groups of 5\*5 pixels for the first result, 6\*6 for the second, etc) +* Z = Change to the step increase between each result (Ex : 5 3 5 will print 3 results while fusing groups of 5\*5 pixels for the first result, 10\*10 for the second, etc) +* ... = Secondary arguments -/!\ The pixelizer needs to be used directly in the folder containing the image. This known limitation may change in the future. /!\ +If not set, X, Y and Z will default to 1, meaning that "pixelize img.png" will output one image with a pixelization level of 1. -1. Print the manual : +2. Secondary arguments to add to modify the basic syntax's behavior : + +* Log the essential of the program activity in terminal : ``` -$ pixelize [OR] pixelize help [OR] pixelize man +$ pixelize [basic syntax] [log] ``` -2. Pixelize an image : +* Log more of the program activity in terminal : ``` -$ pixelize [imageName] [X] [Y] [Z] +$ pixelize [basic syntax] [verb] +``` +* Print every result image in terminal using ANSI codes and register these in the program save directory (HomeDirectory/pixelizer) : +``` +$ pixelize [basic syntax] [print] +``` +* Reduce the size of the image so each pixelized group of colors will display as a pixel. +``` +$ pixelize [basic syntax] [pixel] +``` + +The pixel argument cannot be used while outputting gifs. If both are specified, only the pixel one will be taken into account. +* Output a gif containing all of the result images : +``` +$ pixelize [basic syntax] [gif] [reverse] [full] ``` -* imageName = name of the image, in the current directory, to pixelize. -* X = Width of each group of pixels to fuse (Ex : 5 will fuse every group of 5\*5 pixels from the image) -* Y = Number of results, with increasing X at each result (Ex : 5 3 will print 3 results while fusing groups of 5\*5 pixels for the first result, 6\*6 for the second, etc) -* Z = Change to the step increase between each result (Ex : 5 3 5 will print 3 results while fusing groups of 5\*5 pixels for the first result, 10\*10 for the second, etc) -3. Clear the folder of the result files : +You can add the optional parameter "reverse" or "full" to change the gif animation. + +The "reverse" parameter will reverse the animation. + +The "full" parameter will add the reversed animation to the normal one and make a loop of them. + +4. Clear the folder of the result files : ``` $ pixelize clear ``` -4. Redo the last pixelization : +5. Redo the last pixelization : ``` $ pixelize redo ``` +The "clear" and "redo" parameters can be used with the "log" or "verb" secondary arguments. + + diff --git a/main/console.go b/main/console.go index e274947..006c07a 100644 --- a/main/console.go +++ b/main/console.go @@ -9,7 +9,16 @@ import ( "strconv" ) +const ( + NONE = iota + DEBUG = iota + INFO = iota +) + +var LogLevel = NONE + func param(index int) int { + logIfVerbose(DEBUG, "param[ %v ]", index) var nb int var err error if len(os.Args) < (index + 1) { @@ -35,18 +44,81 @@ func hasParam(param string) bool { return false } -func printInTerminal(img image.Image, pointMin image.Point, pointMax image.Point, jump int) { +func printInTerminal(img image.Image, jump int, index int) { + logIfVerbose(DEBUG, "printInTerminal[ %v ]", jump) bounds := img.Bounds() changedImage := image.NewPaletted(bounds, palette.Plan9) draw.Draw(changedImage, changedImage.Rect, img, bounds.Min, draw.Src) printedText := "\n" - for y := pointMin.Y; y < pointMax.Y; y += jump { - for x := pointMin.X; x < pointMax.X; x += jump { + if hasParam("pixel") { + jump = 1 + } + for y := bounds.Min.Y; y < bounds.Max.Y; y += jump { + for x := bounds.Min.X; x < bounds.Max.X; x += jump { colorA := changedImage.ColorIndexAt(x, y) printedText += "\033[48;5;" + strconv.Itoa(int(colorA)) + "m " } printedText += "\033[0;00m\n" } log.Print(printedText) - saveStr(printedText) + strSuffix := "" + if param(3) > 1 { + strSuffix = strconv.Itoa(index + 1) + } + saveStr(printedText, strSuffix) +} + +func initLogLevel() { + if hasParam("log") { + LogLevel = INFO + } + if hasParam("verb") { + LogLevel = DEBUG + } +} + +func logIfVerbose(level int, str string, obj ...interface{}) { + if LogLevel == DEBUG || (LogLevel == INFO && level == DEBUG) { + print(str, obj...) + } +} + +func print(str string, obj ...interface{}) { + if len(obj) == 0 { + log.Print(str) + } else { + log.Printf(str, obj...) + } +} + +func logIfExists(obj interface{}) { + if obj != nil { + log.Print(obj) + } +} + +func printManual() { + logIfVerbose(DEBUG, "printManual") + log.Print("Basic syntax : " + os.Args[0] + " [imgName] [X] [Y] [Z] [...]") + log.Print("Alt. syntax : " + os.Args[0] + " [first-hand argument]") + log.Print("imgName : Name of the file.") + log.Print("X : Optional - Pixelization level.") + log.Print("Y : Optional - Number of results with increasing pixelization.") + log.Print("Z : Optional - Pixelization level increase between each result.") + log.Print("... : Optional - Secondary arguments in no particular order.") + log.Print("X, Y and Z will be 1 by default if not set.") + log.Print("-----------------------------------") + log.Print("First-hand arguments :") + log.Print("- 'man' or 'help' or nothing : Prints the manual.") + log.Print("- 'clear' : Removes every file generated by the program in the directory.") + log.Print("- 'redo' : Relaunches the command with the last parameters used with the program.") + log.Print("-----------------------------------") + log.Print("Secondary arguments :") + log.Print("- 'pixel' : Reduce the size of the image so each pixelized group of colors will display as a pixel.") + log.Print("- 'print' : Print each result in the terminal, and save a print.log file containing the color codes of the last result.") + log.Print("- 'gif' : Adds a gif file to the outputs, containing each result as a frame. Ignored if 'pixel' is used too.") + log.Print("- 'reverse' : Reverses the gif images' order.") + log.Print("- 'full' : Adds the reverted version to the original gif, instead of replacing the original.") + log.Print("- 'verb' : Log all operations of the program (heavy logging).") + log.Print("- 'log' : Lighter logging, focusing on the essential.") } diff --git a/main/image.go b/main/image.go index 9dc6a3b..39f7bf9 100644 --- a/main/image.go +++ b/main/image.go @@ -7,7 +7,6 @@ import ( "image/draw" "image/gif" "image/png" - "log" "os" "strconv" ) @@ -15,9 +14,10 @@ import ( const GifDelayEachFrame = 20 func launchPixellisator(f *os.File, min int, count int, increase int) { + logIfVerbose(DEBUG, "launchPixellisator[ %v / %v / %v / %v ]", f.Name(), min, count, increase) img, _, errImageDecode := image.Decode(f) + logIfExists(errImageDecode) if errImageDecode != nil { - log.Print("Cannot decode image") return } b := img.Bounds() @@ -35,53 +35,61 @@ func launchPixellisator(f *os.File, min int, count int, increase int) { if count > 1 { name = "result" + strconv.Itoa(i+1) + ".png" } - out, errOutput := os.Create(name) - if errOutput != nil { - log.Print("Cannot create image") - } + out, errOutput := os.Create(FileDirectory + name) + logIfExists(errOutput) errOutputEncode := png.Encode(out, newImg) - if errOutputEncode != nil { - log.Print("Cannot encode image") - } - if hasParam("gif") { + logIfExists(errOutputEncode) + if hasParam("gif") && !hasParam("pixel") { addToGif(&gifImg, newImg) } if hasParam("print") { - printInTerminal(newImg, pointMin, pointMax, min+(i*increase)) + printInTerminal(newImg, min+(i*increase), i) } - if i+1 == count { - if hasParam("gif") { - if hasParam("reverse") { - reverseGif(&gifImg) - } - if hasParam("full") { - addReverseToGif(&gifImg) - } - gifOutput, errGif := os.Create("results.gif") - if errGif != nil { - log.Print("Cannot create gif image") - } - errGifEncode := gif.EncodeAll(gifOutput, &gifImg) - if errGifEncode != nil { - log.Print("Cannot encode gif image") - } + if i+1 == count && hasParam("gif") && !hasParam("pixel") { + if hasParam("reverse") { + reverseGif(&gifImg) + } + if hasParam("full") { + addReverseToGif(&gifImg) } + gifOutput, errGif := os.Create(FileDirectory + "results.gif") + logIfExists(errGif) + errGifEncode := gif.EncodeAll(gifOutput, &gifImg) + logIfExists(errGifEncode) } } } -func repixellise(img image.Image, pointMin image.Point, pointMax image.Point, min int) image.Image { - result := image.NewRGBA(image.Rectangle{Min: pointMin, Max: pointMax}) - for y := pointMin.Y; y < pointMax.Y; y += min { - for x := pointMin.X; x < pointMax.X; x += min { - colorRGBA := avg(img, x, y, min) - squareFill(result, x, y, colorRGBA, min) +func repixellise(img image.Image, pointMin image.Point, pointMax image.Point, pixelization int) image.Image { + logIfVerbose(DEBUG, "repixellise[ %v / %v / %v ]", pointMin, pointMax, pixelization) + i := 0 + j := 0 + var result *image.RGBA + + if hasParam("pixel") { + newPointMax := image.Point{X: (pointMax.X / pixelization) + 1, Y: (pointMax.Y / pixelization) + 1} + result = image.NewRGBA(image.Rectangle{Min: pointMin, Max: newPointMax}) + } else { + result = image.NewRGBA(image.Rectangle{Min: pointMin, Max: pointMax}) + } + for y := pointMin.Y; y < pointMax.Y; y += pixelization { + for x := pointMin.X; x < pointMax.X; x += pixelization { + colorRGBA := avg(img, x, y, pixelization) + if hasParam("pixel") { + result.Set(i, j, colorRGBA) + } else { + squareFill(result, x, y, colorRGBA, pixelization) + } + i++ } + i = 0 + j++ } return result } func avg(img image.Image, x int, y int, count int) color.RGBA { + logIfVerbose(INFO, "avg[ { %v ; %v } / %v ]", x, y, count) total := count * count r := 0 @@ -114,6 +122,7 @@ func avg(img image.Image, x int, y int, count int) color.RGBA { } func squareFill(img *image.RGBA, x int, y int, rgba color.RGBA, min int) { + logIfVerbose(INFO, "squareFill[ { %v ; %v } / %v / %v ]", x, y, rgba, min) for i := 0; i < min; i++ { for j := 0; j < min; j++ { img.Set(x+i, y+j, rgba) @@ -122,6 +131,7 @@ func squareFill(img *image.RGBA, x int, y int, rgba color.RGBA, min int) { } func addToGif(gifImg *gif.GIF, img image.Image) { + logIfVerbose(DEBUG, "addToGif") palettedImage := image.NewPaletted(img.Bounds(), palette.Plan9) draw.Draw(palettedImage, palettedImage.Rect, img, img.Bounds().Min, draw.Src) gifImg.Image = append(gifImg.Image, palettedImage) @@ -129,6 +139,7 @@ func addToGif(gifImg *gif.GIF, img image.Image) { } func addReverseToGif(gifImg *gif.GIF) { + logIfVerbose(DEBUG, "addReverseToGif") for i := len(gifImg.Image) - 2; i > 0; i-- { gifImg.Image = append(gifImg.Image, gifImg.Image[i]) gifImg.Delay = append(gifImg.Delay, GifDelayEachFrame) @@ -136,6 +147,7 @@ func addReverseToGif(gifImg *gif.GIF) { } func reverseGif(gifImg *gif.GIF) { + logIfVerbose(DEBUG, "reverseGif") var images []*image.Paletted for i := len(gifImg.Image) - 1; i >= 0; i-- { images = append(images, gifImg.Image[i]) diff --git a/main/main.go b/main/main.go index 164573b..ee39f09 100644 --- a/main/main.go +++ b/main/main.go @@ -3,34 +3,51 @@ package main import ( _ "image/jpeg" _ "image/png" - "log" "os" ) +var WorkingDirectory string +var FileDirectory string +var SaveDir string +var AppName = "pixelizer" + func main() { if len(os.Args) < 2 || os.Args[1] == "man" || os.Args[1] == "help" { - log.Print("Use : " + os.Args[0] + " [1] [2] [3] [4]") - log.Print("1 : Name of the file") - log.Print("2 : Pixelization level") - log.Print("3 : Optional - Number of results with increasing pixelization") - log.Print("4 : Optional - Pixelization level increase between each result") + printManual() return } + initLogLevel() mainParam := os.Args[1] + if mainParam == "clear" { + setSaveDir() + readAndGetParams() + setPreviousWorkingDirectory() + addWorkingDirToArgs() + setFileDirectory() clearResults() } else if mainParam == "redo" { - img, min, count, increase := readAndGetParams() - f, err := os.Open(img) - if err != nil { - log.Print("Cannot find this file.") + setSaveDir() + readAndGetParams() + setPreviousWorkingDirectory() + addWorkingDirToArgs() + setFileDirectory() + img := os.Args[1] + min := param(2) + count := param(3) + increase := param(4) + f, errOpenImg := os.Open(FileDirectory + getNameOfFile(img)) + logIfExists(errOpenImg) + if errOpenImg != nil { return } launchPixellisator(f, min, count, increase) } else { - f, err := os.Open(mainParam) - if err != nil { - log.Print("Cannot find this file.") + setDirVariables() + os.Args = append(os.Args, WorkingDirectory) + f, errOpenImage := os.Open(mainParam) + logIfExists(errOpenImage) + if errOpenImage != nil { return } defer f.Close() @@ -38,6 +55,6 @@ func main() { count := param(3) increase := param(4) launchPixellisator(f, min, count, increase) - save(min, count, increase) + save() } } diff --git a/main/path.go b/main/path.go new file mode 100644 index 0000000..712d97e --- /dev/null +++ b/main/path.go @@ -0,0 +1,92 @@ +package main + +import ( + "os" + "os/user" + "strings" +) + +func setDirVariables() { + logIfVerbose(DEBUG, "setDirVariables") + setSaveDir() + setWorkingDirectory() + addWorkingDirToArgs() + setFileDirectory() +} + +func setFileDirectory() { + logIfVerbose(DEBUG, "setFileDirectory") + imageDir := getPathOf(os.Args[1]) + if imageDir[0] == '/' { + FileDirectory = imageDir + } else { + FileDirectory = WorkingDirectory + imageDir + } +} + +func setPreviousWorkingDirectory() { + logIfVerbose(DEBUG, "setPreviousWorkingDirectory") + WorkingDirectory = os.Args[len(os.Args)-1] +} + +func setWorkingDirectory() { + logIfVerbose(DEBUG, "setWorkingDirectory") + var errGettingPath error + WorkingDirectory, errGettingPath = os.Getwd() + logIfExists(errGettingPath) + if errGettingPath != nil { + return + } + WorkingDirectory += "/" +} + +func getPathOf(filePath string) string { + logIfVerbose(DEBUG, "getPathOf[ %v ]", filePath) + split := strings.Split(filePath, "/") + str := "" + if len(split) > 1 { + if filePath[0] == '/' { + str += "/" + } + for i := range split { + if i < len(split)-1 { + str += split[i] + str += "/" + } + } + } else { + str = "./" + } + return str +} + +func getNameOfFile(filePath string) string { + logIfVerbose(DEBUG, "getNameOfFile[ %v ]", filePath) + split := strings.Split(filePath, "/") + if len(split)-1 >= 0 { + return split[len(split)-1] + } else { + return split[0] + } +} + +func setSaveDir() { + logIfVerbose(DEBUG, "setSaveDir") + usr, errGetUser := user.Current() + logIfExists(errGetUser) + SaveDir = usr.HomeDir + "/" + AppName + "/" + createPixelizerDir() +} + +func addWorkingDirToArgs() { + logIfVerbose(DEBUG, "addWorkingDirToArgs") + os.Args = append(os.Args, WorkingDirectory) +} + +func createPixelizerDir() { + logIfVerbose(DEBUG, "createPixelizerDir") + _, err := os.Stat(SaveDir) + if err != nil { + os.Mkdir(SaveDir, os.ModePerm) + } +} diff --git a/main/saves.go b/main/saves.go index 17a1644..abd4126 100644 --- a/main/saves.go +++ b/main/saves.go @@ -2,70 +2,50 @@ package main import ( "bufio" - "log" "os" - "path" - "path/filepath" - "strconv" "strings" ) const ResultName = "result" const SaveName = "log" -const TerminalPrintName = "print.log" +const TerminalPrintName = "print" -func save(min int, count int, increase int) { - dir, err := filepath.Abs(filepath.Dir(os.Args[0])) - if err != nil { - log.Print("Cannot find program directory") - } - f, err := os.Create(path.Join(path.Dir(dir), SaveName)) - if err != nil { - log.Print("Cannot create save file") - } +func save() { + logIfVerbose(DEBUG, "save") + f, errCreateFile := os.Create(SaveDir + SaveName) + logIfExists(errCreateFile) defer f.Close() - f.Write([]byte(os.Args[1] + ";" + strconv.Itoa(min) + ";" + strconv.Itoa(count) + ";" + strconv.Itoa(increase))) -} + strSave := "" -func saveStr(str string) { - f, err := os.Create(TerminalPrintName) - if err != nil { - log.Print("Cannot create print save file") + for i := range os.Args { + strSave += os.Args[i] + if i < len(os.Args)-1 { + strSave += ";" + } } + f.Write([]byte(strSave)) +} + +func saveStr(str string, suffix string) { + logIfVerbose(DEBUG, "saveStr") + logIfVerbose(INFO, "Saving color codes to "+SaveDir+TerminalPrintName+suffix+".log") + f, errCreateFile := os.Create(SaveDir + "/" + TerminalPrintName + suffix + ".log") + logIfExists(errCreateFile) defer f.Close() f.Write([]byte(str)) } -func readAndGetParams() (string, int, int, int) { - dir, err := filepath.Abs(filepath.Dir(os.Args[0])) - if err != nil { - log.Panic("Cannot find the program directory.") - } - f, err := os.Open(path.Join(path.Dir(dir), SaveName)) - if err != nil { - log.Panic("Cannot find the save file.") - } +func readAndGetParams() { + logIfVerbose(DEBUG, "readAndGetParams") + f, errOpenSaveDir := os.Open(SaveDir + SaveName) + logIfExists(errOpenSaveDir) scanner := bufio.NewScanner(f) scanner.Scan() strSaved := scanner.Text() splitStr := strings.Split(strSaved, ";") - imgName := splitStr[0] - min := splitStr[1] - count := splitStr[2] - increase := splitStr[3] - resultMin, err := strconv.Atoi(min) - if err != nil { - resultMin = 1 - } - resultCount, err := strconv.Atoi(count) - if err != nil { - resultCount = 1 + os.Args = []string{} + for i := range splitStr { + os.Args = append(os.Args, splitStr[i]) } - resultIncrease, err := strconv.Atoi(increase) - if err != nil { - resultIncrease = 1 - } - - return imgName, resultMin, resultCount, resultIncrease } diff --git a/main/tools.go b/main/tools.go index 877f86f..4e420d5 100644 --- a/main/tools.go +++ b/main/tools.go @@ -1,34 +1,36 @@ package main import ( - "fmt" - "log" "os" ) func clearResults() { - dirRead, err := os.Open(".") - if err != nil { - log.Print("Cannot open directory") - } - dirFiles, err := dirRead.Readdir(0) - if err != nil { - log.Print("Cannot read directory") - } - for index := range dirFiles { - fileHere := dirFiles[index] - - name := fileHere.Name() + logIfVerbose(DEBUG, "clearResults") + removeFilesInDir(FileDirectory, ResultName) + removeFilesInDir(SaveDir, TerminalPrintName) +} +func removeFilesInDir(dir string, filesPrefix string) { + logIfVerbose(DEBUG, "removeFilesInDir[ %v / %v ]", dir, filesPrefix) + dirRead, errOpenDir := os.Open(dir) + logIfExists(errOpenDir) + dirFiles, errReadDir := dirRead.Readdir(0) + logIfExists(errReadDir) + for index := range dirFiles { + fileInDir := dirFiles[index] + name := fileInDir.Name() isSame := true - for i := range ResultName { - if name[i] != ResultName[i] { + for i := range filesPrefix { + if len(name) < len(filesPrefix) || name[i] != filesPrefix[i] { isSame = false } } if isSame { - os.Remove(name) - fmt.Println("Removed file:", name) + errRemoveFile := os.Remove(dir + name) + logIfExists(errRemoveFile) + if errRemoveFile == nil { + logIfVerbose(DEBUG, "Removed file: %v", name) + } } } }