mirror of
https://github.com/TheZoraiz/ascii-image-converter.git
synced 2026-05-17 00:45:58 +03:00
Changed --save flag to --save-img to save as image, added separate flag for text saving
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
pics/
|
||||
build/
|
||||
snap/
|
||||
dist/
|
||||
dist/
|
||||
build.sh
|
||||
todo.txt
|
||||
44
README.md
44
README.md
@@ -24,7 +24,7 @@ Image formats currently supported:
|
||||
* [Flags](#flags)
|
||||
- [Library Usage](#library-usage)
|
||||
- [Contributing](#contributing)
|
||||
- [Packages used](#packages-used)
|
||||
- [Packages Used](#packages-used)
|
||||
- [License](#license)
|
||||
|
||||
## Installation
|
||||
@@ -189,22 +189,32 @@ ascii-image-converter [image paths/urls] --negative
|
||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/negative.gif">
|
||||
</p>
|
||||
|
||||
#### --save OR -s
|
||||
#### --save-img OR -s
|
||||
|
||||
Note: Don't immediately append another flag with -s
|
||||
|
||||
Save ascii art in the format `<image-name>.<image-extension>-ascii-art.txt` in the directory path passed to the flag.
|
||||
Saves the ascii as a PNG image in the with the name `<image-name>-ascii-art.png` in the directory path passed to the flag. Can work with both --color and --negative flag.
|
||||
|
||||
Example for current directory:
|
||||
|
||||
```
|
||||
ascii-image-converter [image paths/urls] --save .
|
||||
ascii-image-converter [image paths/urls] --save-img .
|
||||
# Or
|
||||
ascii-image-converter [image paths/urls] -s .
|
||||
```
|
||||
|
||||
#### --save-txt
|
||||
|
||||
Similar to --save-img as but it creates a TXT file with the name `<image-name>-ascii-art.txt` in the directory path passed to the flag. Only saves uncolored text.
|
||||
|
||||
Example for current directory:
|
||||
|
||||
```
|
||||
ascii-image-converter [image paths/urls] --save-txt .
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/save.gif">
|
||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/save-image-txt-demo.gif">
|
||||
</p>
|
||||
|
||||
#### --formats OR -f
|
||||
@@ -227,10 +237,6 @@ ascii-image-converter [image paths/urls] --flipX
|
||||
ascii-image-converter [image paths/urls] -x
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/flipx.gif">
|
||||
</p>
|
||||
|
||||
#### --flipY OR -y
|
||||
Flip the ascii art vertically on the terminal.
|
||||
|
||||
@@ -241,21 +247,23 @@ ascii-image-converter [image paths/urls] -y
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/flipy.gif">
|
||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/flipx-flipy.gif">
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
You can combine flags as well. Following command outputs colored and negative ascii art, flips ascii art horizontally and vertically, with fixed 100 by 30 character dimensions, custom defined ascii characters " .-=+#@" and saves the output in current directory as well.
|
||||
You can combine flags as well. Following command outputs colored and negative ascii art, flips ascii art horizontally and vertically, with fixed 60 by 30 character dimensions, custom defined ascii characters " .-=+#@" and saves the a generated image and .txt file in current directory as well.
|
||||
|
||||
```
|
||||
ascii-image-converter [image paths/urls] -Cnxyd 100,30 -m " .-=+#@" -s ./
|
||||
ascii-image-converter [image paths/urls] -Cnxyd 60,30 -m " .-=+#@" -s . --save-txt .
|
||||
```
|
||||
<br>
|
||||
|
||||
## Library Usage
|
||||
|
||||
First import the library with:
|
||||
Note: The library may throw errors during Go tests due to some unresolved bugs with the [consolesize-go](https://github.com/nathan-fiscaletti/consolesize-go) package (Only during tests, not main program execution). Looking into this issue.
|
||||
|
||||
First, install the library with:
|
||||
```
|
||||
go get github.com/TheZoraiz/ascii-image-converter/aic_package
|
||||
```
|
||||
@@ -266,6 +274,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/TheZoraiz/ascii-image-converter/aic_package"
|
||||
)
|
||||
|
||||
@@ -279,7 +288,8 @@ func main() {
|
||||
// For clarity, all flags are covered in this example, but you can use specific ones.
|
||||
flags["complex"] = true // Use complex character set
|
||||
flags["dimensions"] = []int{50, 25} // 50 by 25 ascii art size
|
||||
flags["savePath"] = "." // Saves to current directory
|
||||
flags["saveTxtPath"]: ".", // Save generated text in same directory
|
||||
flags["saveImagePath"]: ".", // Save generated image in same directory
|
||||
flags["negative"] = true // Ascii art will have negative color-depth
|
||||
flags["colored"] = true // Keep colors from original image
|
||||
flags["customMap"] = " .-=+#@" // Starting from darkest to brightest shades. This overrites "complex" flag
|
||||
@@ -302,10 +312,12 @@ func main() {
|
||||
|
||||
You can fork the project and implement any changes you want for a pull request. However, for major changes, please open an issue first to discuss what you would like to implement.
|
||||
|
||||
## Packages used
|
||||
## Packages Used
|
||||
|
||||
[github.com/spf13/cobra](https://github.com/spf13/cobra)
|
||||
|
||||
[github.com/fogleman/gg](https://github.com/fogleman/gg)
|
||||
|
||||
[github.com/mitchellh/go-homedir](https://github.com/mitchellh/go-homedir)
|
||||
|
||||
[github.com/nathan-fiscaletti/consolesize-go](https://github.com/nathan-fiscaletti/consolesize-go)
|
||||
@@ -318,4 +330,4 @@ You can fork the project and implement any changes you want for a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
|
||||
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
|
||||
BIN
aic_package/RobotoMono-Regular.ttf
Normal file
BIN
aic_package/RobotoMono-Regular.ttf
Normal file
Binary file not shown.
@@ -51,6 +51,7 @@ func DefaultFlags() map[string]interface{} {
|
||||
"customMap": "",
|
||||
"flipX": false,
|
||||
"flipY": false,
|
||||
"saveImage": false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +65,8 @@ The "flags" argument should be declared as follows before passing:
|
||||
flags := map[string]interface{}{
|
||||
"complex": bool, // Pass true for using complex character set
|
||||
"dimensions": []int, // Pass 2 integer dimensions. Pass nil to ignore
|
||||
"savePath": string, // System path to save the ascii art string. Pass "" to ignore
|
||||
"saveTxtPath": string, // System path to save the ascii art string as a .txt file. Pass "" to ignore
|
||||
"saveImagePath": string, // System path to save the ascii art string as a .txt file. Pass "" to ignore
|
||||
"negative": bool, // Pass true for negative color-depth ascii art
|
||||
"colored": bool, // Pass true for returning colored ascii string
|
||||
"customMap": string, // Custom map of ascii chars e.g. " .-+#@" . Nullifies "complex" flag. Pass "" to ignore.
|
||||
@@ -74,25 +76,22 @@ The "flags" argument should be declared as follows before passing:
|
||||
*/
|
||||
func ConvertImage(imagePath string, flags map[string]interface{}) (string, error) {
|
||||
|
||||
complex := flags["complex"].(bool)
|
||||
var dimensions []int
|
||||
if flags["dimensions"] == nil {
|
||||
dimensions = nil
|
||||
} else {
|
||||
dimensions = flags["dimensions"].([]int)
|
||||
}
|
||||
savePath := flags["savePath"].(string)
|
||||
|
||||
complex := flags["complex"].(bool)
|
||||
saveTxtPath := flags["saveTxtPath"].(string)
|
||||
saveImagePath := flags["saveImagePath"].(string)
|
||||
negative := flags["negative"].(bool)
|
||||
colored := flags["colored"].(bool)
|
||||
customMap := flags["customMap"].(string)
|
||||
flipX := flags["flipX"].(bool)
|
||||
flipY := flags["flipY"].(bool)
|
||||
|
||||
numberOfDimensions := len(dimensions)
|
||||
if dimensions != nil && numberOfDimensions != 2 {
|
||||
return "", fmt.Errorf("requires 2 dimensions, got %v", numberOfDimensions)
|
||||
}
|
||||
|
||||
// Declared at the start since some variables are initially used in conditional blocks
|
||||
var (
|
||||
pic *os.File
|
||||
@@ -109,7 +108,7 @@ func ConvertImage(imagePath string, flags map[string]interface{}) (string, error
|
||||
|
||||
retrievedImage, err := http.Get(imagePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't fetching image: %v", err)
|
||||
return "", fmt.Errorf("can't fetch image: %v", err)
|
||||
}
|
||||
|
||||
urlImgBytes, err = ioutil.ReadAll(retrievedImage.Body)
|
||||
@@ -120,12 +119,15 @@ func ConvertImage(imagePath string, flags map[string]interface{}) (string, error
|
||||
|
||||
urlImgName = path.Base(imagePath)
|
||||
fmt.Printf(" \r") // To erase "Fetching image from url..." text from console
|
||||
|
||||
} else {
|
||||
|
||||
pic, err = os.Open(imagePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to open file: %v", err)
|
||||
}
|
||||
defer pic.Close()
|
||||
|
||||
}
|
||||
|
||||
var imData image.Image
|
||||
@@ -136,27 +138,36 @@ func ConvertImage(imagePath string, flags map[string]interface{}) (string, error
|
||||
imData, _, err = image.Decode(pic)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error decoding %v. %v", imagePath, err)
|
||||
return "", fmt.Errorf("can't decode %v: %v", imagePath, err)
|
||||
}
|
||||
|
||||
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, flipX, flipY)
|
||||
// x and y are height and width of resulting ascii art
|
||||
// These numbers are important for creating png image to save
|
||||
imgSet, x, y, err := imgManip.ConvertToAsciiPixels(imData, dimensions, flipX, flipY)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%v", err)
|
||||
}
|
||||
|
||||
asciiSet := imgManip.ConvertToAscii(imgSet, negative, colored, complex, customMap)
|
||||
|
||||
ascii := flattenAscii(asciiSet, colored)
|
||||
|
||||
// Save ascii art before printing it, if --save flag is passed
|
||||
if savePath != "" {
|
||||
if err := saveAsciiArt(asciiSet, imagePath, savePath, urlImgName); err != nil {
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
os.Exit(0) // Because this error will be thrown for every image passed to this function if we use "return"
|
||||
// Save ascii art as .png image before printing it, if --save-img flag is passed
|
||||
if saveImagePath != "" {
|
||||
if err := createImageToSave(asciiSet, x, y, colored, saveImagePath, imagePath, urlImgName); err != nil {
|
||||
return "", fmt.Errorf("can't save file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Save ascii art as .txt file before printing it, if --save-txt flag is passed
|
||||
if saveTxtPath != "" {
|
||||
if err := saveAsciiArt(asciiSet, imagePath, saveTxtPath, urlImgName); err != nil {
|
||||
return "", fmt.Errorf("can't save file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ascii := flattenAscii(asciiSet, colored)
|
||||
|
||||
result := strings.Join(ascii, "\n")
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -170,14 +181,14 @@ func checkOS() string {
|
||||
|
||||
// flattenAscii flattens a two-dimensional grid of ascii characters into a one dimension
|
||||
// of lines of ascii
|
||||
func flattenAscii(asciiSet [][]imgManip.AsciiChar, color bool) []string {
|
||||
func flattenAscii(asciiSet [][]imgManip.AsciiChar, colored bool) []string {
|
||||
var ascii []string
|
||||
|
||||
for _, line := range asciiSet {
|
||||
var tempAscii []string
|
||||
|
||||
for i := 0; i < len(line); i++ {
|
||||
if color {
|
||||
if colored {
|
||||
tempAscii = append(tempAscii, line[i].Colored)
|
||||
} else {
|
||||
tempAscii = append(tempAscii, line[i].Simple)
|
||||
@@ -191,10 +202,10 @@ func flattenAscii(asciiSet [][]imgManip.AsciiChar, color bool) []string {
|
||||
}
|
||||
|
||||
func saveAsciiArt(asciiSet [][]imgManip.AsciiChar, imagePath, savePath, urlImgName string) error {
|
||||
// To make sure uncolored ascii art is the one saved
|
||||
// To make sure uncolored ascii art is the one saved as .txt
|
||||
saveAscii := flattenAscii(asciiSet, false)
|
||||
|
||||
saveFileName, err := createSaveFileName(imagePath, urlImgName)
|
||||
saveFileName, err := createSaveFileName(imagePath, urlImgName, ".txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -218,9 +229,12 @@ func saveAsciiArt(asciiSet [][]imgManip.AsciiChar, imagePath, savePath, urlImgNa
|
||||
}
|
||||
}
|
||||
|
||||
func createSaveFileName(imagePath string, urlImgName string) (string, error) {
|
||||
func createSaveFileName(imagePath, urlImgName, newExtension string) (string, error) {
|
||||
if urlImgName != "" {
|
||||
return urlImgName + "-ascii-art.txt", nil
|
||||
currExt := path.Ext(urlImgName)
|
||||
newName := urlImgName[:len(urlImgName)-len(currExt)] // e.g. Grabs myImage from myImage.jpeg
|
||||
|
||||
return newName + "-ascii-art.png", nil
|
||||
}
|
||||
|
||||
fileInfo, err := os.Stat(imagePath)
|
||||
@@ -233,5 +247,5 @@ func createSaveFileName(imagePath string, urlImgName string) (string, error) {
|
||||
newName := currName[:len(currName)-len(currExt)] // e.g. Grabs myImage from myImage.jpeg
|
||||
|
||||
// Something like myImage.jpeg-ascii-art.txt
|
||||
return newName + "." + currExt[1:] + "-ascii-art.txt", nil
|
||||
return newName + "-ascii-art" + newExtension, nil
|
||||
}
|
||||
|
||||
148
aic_package/create_ascii_image.go
Normal file
148
aic_package/create_ascii_image.go
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright © 2021 Zoraiz Hassan <hzoraiz8@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package aic_package
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"image"
|
||||
"image/color"
|
||||
"os"
|
||||
|
||||
imgManip "github.com/TheZoraiz/ascii-image-converter/image_manipulation"
|
||||
"github.com/golang/freetype/truetype"
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
)
|
||||
|
||||
// This file mostly has spaghetti code lol.
|
||||
// Will work on organizing it into a readable format soon. Apologies...
|
||||
|
||||
// To embed font directly into the binary, instead of packaging it as a separate file
|
||||
//go:embed RobotoMono-Regular.ttf
|
||||
var embeddedFontFile []byte
|
||||
|
||||
func createImageToSave(asciiArt [][]imgManip.AsciiChar, x, y int, colored bool, saveImagePath, imagePath, urlImgName string) error {
|
||||
|
||||
// IMPORTANT NOTE:
|
||||
// The raw numbers from here on are strictly experimental
|
||||
// and are used because they just happened to work with
|
||||
// the tallest and widest images I could find, which cover
|
||||
// the extreme cases of possible images encountered.
|
||||
|
||||
// Multipying with respect to font size. I altered the numbers
|
||||
// until extreme cases' output images becaume right
|
||||
x = int(12.6 * float32(x))
|
||||
|
||||
y = 14 * y
|
||||
y = y * 2
|
||||
|
||||
// To give small extra margins near the edges
|
||||
y += 10
|
||||
x += 10
|
||||
|
||||
tempImg := image.NewRGBA(image.Rect(0, 0, x, y))
|
||||
|
||||
imgWidth := tempImg.Bounds().Dx()
|
||||
imgHeight := tempImg.Bounds().Dy()
|
||||
|
||||
dc := gg.NewContext(imgWidth, imgHeight)
|
||||
|
||||
// Set image background as black
|
||||
dc.SetRGB(0, 0, 0)
|
||||
dc.Clear()
|
||||
|
||||
dc.DrawImage(tempImg, 0, 0)
|
||||
|
||||
// Load embedded font
|
||||
tempFont, err := truetype.Parse(embeddedFontFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
robotoFontFace := truetype.NewFace(tempFont, &truetype.Options{Size: 21.0})
|
||||
|
||||
dc.SetFontFace(robotoFontFace)
|
||||
|
||||
// Font color of text on picture is white by default
|
||||
dc.SetColor(color.White)
|
||||
|
||||
// Pointer to track y-axis on the image frame
|
||||
yImgPointer := 5.0
|
||||
|
||||
// These nested loops print each character in asciArt 2D slice separately
|
||||
// so that their RGB colors can be maintained in the resulting image
|
||||
for _, line := range asciiArt {
|
||||
|
||||
// Pointer to track x-axis on the image frame
|
||||
xImgPointer := 5.0
|
||||
|
||||
for _, char := range line {
|
||||
|
||||
r := uint8(char.RgbValue[0])
|
||||
g := uint8(char.RgbValue[1])
|
||||
b := uint8(char.RgbValue[2])
|
||||
|
||||
if colored {
|
||||
// Simple put, dc.SetColor() sets color for EACH character before printing it
|
||||
dc.SetColor(color.RGBA{r, g, b, 255})
|
||||
}
|
||||
|
||||
dc.DrawStringWrapped(char.Simple, xImgPointer, yImgPointer, 0, 0, float64(x), 1.8, gg.AlignLeft)
|
||||
|
||||
// Incremet x-axis pointer character so new one can be printed after it
|
||||
xImgPointer += 12.6
|
||||
}
|
||||
|
||||
dc.DrawStringWrapped("\n", xImgPointer, yImgPointer, 0, 0, float64(x), 1.8, gg.AlignLeft)
|
||||
|
||||
// Incremet pointer for y axis after every line printed, so
|
||||
// new line can start at below the previous one on next iteration
|
||||
yImgPointer += 28
|
||||
}
|
||||
|
||||
imageName, err := createSaveFileName(imagePath, urlImgName, ".png")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fullPathName, err := getFullSavePath(imageName, saveImagePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dc.SavePNG(fullPathName)
|
||||
}
|
||||
|
||||
// Returns path with the file name concatenated to it
|
||||
func getFullSavePath(imageName, saveImagePath string) (string, error) {
|
||||
savePathLastChar := string(saveImagePath[len(saveImagePath)-1])
|
||||
|
||||
// Check if path is closed with appropriate path separator (depending on OS)
|
||||
if savePathLastChar != string(os.PathSeparator) {
|
||||
if checkOS() == "linux" {
|
||||
saveImagePath += "/"
|
||||
} else {
|
||||
saveImagePath += "\\"
|
||||
}
|
||||
}
|
||||
|
||||
// If path exists
|
||||
if _, err := os.Stat(saveImagePath); !os.IsNotExist(err) {
|
||||
return saveImagePath + imageName, nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
81
cmd/root.go
81
cmd/root.go
@@ -21,15 +21,6 @@ import (
|
||||
|
||||
"github.com/TheZoraiz/ascii-image-converter/aic_package"
|
||||
|
||||
// Image format initialization
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
|
||||
// Image format initialization
|
||||
_ "golang.org/x/image/bmp"
|
||||
_ "golang.org/x/image/tiff"
|
||||
_ "golang.org/x/image/webp"
|
||||
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@@ -37,22 +28,23 @@ import (
|
||||
|
||||
var (
|
||||
// Flags
|
||||
cfgFile string
|
||||
complex bool
|
||||
dimensions []int
|
||||
savePath string
|
||||
negative bool
|
||||
formatsTrue bool
|
||||
colored bool
|
||||
customMap string
|
||||
flipX bool
|
||||
flipY bool
|
||||
cfgFile string
|
||||
complex bool
|
||||
dimensions []int
|
||||
saveTxtPath string
|
||||
saveImagePath string
|
||||
negative bool
|
||||
formatsTrue bool
|
||||
colored bool
|
||||
customMap string
|
||||
flipX bool
|
||||
flipY bool
|
||||
|
||||
// Root commands
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "ascii-image-converter [image paths/urls]",
|
||||
Short: "Converts images into ascii art",
|
||||
Version: "1.2.6",
|
||||
Version: "1.3.0",
|
||||
Long: "This tool converts images into ascii art and prints them on the terminal.\nFurther configuration can be managed with flags.",
|
||||
|
||||
// Not RunE since help text is getting larger and seeing it for every error impacts user experience
|
||||
@@ -65,7 +57,6 @@ var (
|
||||
|
||||
if len(args) < 1 {
|
||||
fmt.Printf("Error: Need at least 1 image path/url\n\n")
|
||||
cmd.Help()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,27 +65,38 @@ var (
|
||||
return
|
||||
}
|
||||
|
||||
flags := map[string]interface{}{
|
||||
"complex": complex,
|
||||
"dimensions": dimensions,
|
||||
"savePath": savePath,
|
||||
"negative": negative,
|
||||
"colored": colored,
|
||||
"customMap": customMap,
|
||||
"flipX": flipX,
|
||||
"flipY": flipY,
|
||||
numberOfDimensions := len(dimensions)
|
||||
if dimensions != nil && numberOfDimensions != 2 {
|
||||
fmt.Printf("Error: requires 2 dimensions, got %v\n\n", numberOfDimensions)
|
||||
return
|
||||
}
|
||||
|
||||
// fmt.Println(flags)
|
||||
flags := map[string]interface{}{
|
||||
"complex": complex,
|
||||
"dimensions": dimensions,
|
||||
"saveTxtPath": saveTxtPath,
|
||||
"saveImagePath": saveImagePath,
|
||||
"negative": negative,
|
||||
"colored": colored,
|
||||
"customMap": customMap,
|
||||
"flipX": flipX,
|
||||
"flipY": flipY,
|
||||
}
|
||||
|
||||
for _, imagePath := range args {
|
||||
|
||||
if asciiArt, err := aic_package.ConvertImage(imagePath, flags); err == nil {
|
||||
fmt.Printf("%s", asciiArt)
|
||||
} else {
|
||||
fmt.Printf("Error: %v", err)
|
||||
}
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
|
||||
// Because this error will then be thrown for every image path/url passed
|
||||
// if save path is invalid
|
||||
if err.Error()[:15] == "can't save file" {
|
||||
fmt.Println()
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
},
|
||||
@@ -113,22 +115,25 @@ func Execute() {
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.PersistentFlags().SortFlags = false
|
||||
|
||||
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ascii-image-converter.yaml)")
|
||||
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with the colors from original image\n(Can work with the -n flag)\n")
|
||||
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with the colors from original image\n(Can work with the --negative flag)\n")
|
||||
rootCmd.PersistentFlags().BoolVarP(&complex, "complex", "c", false, "Display ascii characters in a larger range\nMay result in higher quality\n")
|
||||
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length\ne.g. -d 100,30 (defaults to terminal height)\n")
|
||||
rootCmd.PersistentFlags().BoolVarP(&formatsTrue, "formats", "f", false, "Display supported image formats\n")
|
||||
rootCmd.PersistentFlags().StringVarP(&customMap, "map", "m", "", "Give custom ascii characters to map against\nOrdered from darkest to lightest\ne.g. -m \" .-+#@\" (Quotation marks excluded from map)\n(Cancels --complex flag)\n")
|
||||
rootCmd.PersistentFlags().BoolVarP(&negative, "negative", "n", false, "Display ascii art in negative colors\n(Can work with the --color flag)\n")
|
||||
rootCmd.PersistentFlags().StringVarP(&savePath, "save", "s", "", "Save ascii art in the format:\n<image-name>.<image-extension>-ascii-art.txt\nFile will be saved in passed path\n(pass . for current directory)\n")
|
||||
rootCmd.PersistentFlags().BoolVarP(&flipX, "flipX", "x", false, "Flip ascii art horizontally\n")
|
||||
rootCmd.PersistentFlags().BoolVarP(&flipY, "flipY", "y", false, "Flip ascii art vertically\n")
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&saveImagePath, "save-img", "s", "", "Save ascii art in a .png file\nFormat: <image-name>-ascii-art.png\nImage will be saved in passed path\n(pass . for current directory)\n")
|
||||
rootCmd.PersistentFlags().StringVar(&saveTxtPath, "save-txt", "", "Save ascii art in the a .txt file\nFormat: <image-name>-ascii-art.txt\nFile will be saved in passed path\n(pass . for current directory)\n")
|
||||
|
||||
defaultUsageTemplate := rootCmd.UsageTemplate()
|
||||
rootCmd.SetUsageTemplate("\nCopyright © 2021 Zoraiz Hassan <hzoraiz8@gmail.com>\n" +
|
||||
rootCmd.SetUsageTemplate(defaultUsageTemplate + "\nCopyright © 2021 Zoraiz Hassan <hzoraiz8@gmail.com>\n" +
|
||||
"Distributed under the Apache License Version 2.0 (Apache-2.0)\n" +
|
||||
"For further details, visit https://github.com/TheZoraiz/ascii-image-converter\n\n" +
|
||||
defaultUsageTemplate)
|
||||
"For further details, visit https://github.com/TheZoraiz/ascii-image-converter\n\n")
|
||||
}
|
||||
|
||||
// initConfig reads in config file and ENV variables if set.
|
||||
|
||||
4
go.mod
4
go.mod
@@ -4,7 +4,9 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
@@ -18,7 +20,7 @@ require (
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.7.1
|
||||
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e
|
||||
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
)
|
||||
|
||||
8
go.sum
8
go.sum
@@ -39,6 +39,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
@@ -50,6 +52,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
@@ -272,8 +276,8 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q=
|
||||
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk=
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
||||
@@ -113,8 +113,9 @@ var asciiTableDetailed = map[int]string{
|
||||
const MAX_VAL float32 = 65535
|
||||
|
||||
type AsciiChar struct {
|
||||
Colored string
|
||||
Simple string
|
||||
Colored string
|
||||
Simple string
|
||||
RgbValue []uint32
|
||||
}
|
||||
|
||||
// Converts the 2D AsciiPixel slice of image data (each instance representing each pixel of original image)
|
||||
@@ -172,6 +173,9 @@ func ConvertToAscii(imgSet [][]AsciiPixel, negative bool, colored bool, complex
|
||||
g = 255 - g
|
||||
b = 255 - b
|
||||
|
||||
// To preserve negative rgb values for saving png image later down the line, since it uses imgSet
|
||||
imgSet[i][j].rgbValue = []uint32{uint32(r), uint32(g), uint32(b)}
|
||||
|
||||
tempInt = (len(chosenTable) - 1) - tempInt
|
||||
}
|
||||
|
||||
@@ -184,6 +188,8 @@ func ConvertToAscii(imgSet [][]AsciiPixel, negative bool, colored bool, complex
|
||||
char.Colored = color.Sprintf("<fg="+rStr+","+gStr+","+bStr+">%v</>", chosenTable[tempInt])
|
||||
char.Simple = chosenTable[tempInt]
|
||||
|
||||
char.RgbValue = imgSet[i][j].rgbValue
|
||||
|
||||
tempSlice = append(tempSlice, char)
|
||||
}
|
||||
result[i] = tempSlice
|
||||
|
||||
@@ -36,7 +36,7 @@ type AsciiPixel struct {
|
||||
//
|
||||
// The returned 2D AsciiPixel slice contains each corresponding pixel's values. Grayscale value
|
||||
// ranges from 0 to 65535, while RGB values are separate.
|
||||
func ConvertToAsciiPixels(img image.Image, dimensions []int, flipX, flipY bool) ([][]AsciiPixel, error) {
|
||||
func ConvertToAsciiPixels(img image.Image, dimensions []int, flipX, flipY bool) ([][]AsciiPixel, int, int, error) {
|
||||
|
||||
var asciiWidth, asciiHeight int
|
||||
var smallImg image.Image
|
||||
@@ -77,9 +77,10 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, flipX, flipY bool)
|
||||
// If there are passed dimensions, check whether the width exceeds terminal width
|
||||
if len(dimensions) > 0 {
|
||||
defaultTermWidth, _ := consolesize.GetConsoleSize()
|
||||
|
||||
defaultTermWidth -= 1
|
||||
if dimensions[0] > defaultTermWidth {
|
||||
return nil, fmt.Errorf("set width is larger than terminal width")
|
||||
return nil, 0, 0, fmt.Errorf("set width is larger than terminal width")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +124,7 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, flipX, flipY bool)
|
||||
imgSet = reverse(imgSet, flipX, flipY)
|
||||
}
|
||||
|
||||
return imgSet, nil
|
||||
return imgSet, asciiWidth, asciiHeight, nil
|
||||
}
|
||||
|
||||
func reverse(imgSet [][]AsciiPixel, flipX, flipY bool) [][]AsciiPixel {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: ascii-image-converter
|
||||
base: core18
|
||||
version: "1.2.6"
|
||||
version: "1.3.0"
|
||||
summary: Converts images into ascii art
|
||||
description: |
|
||||
This tool converts images into ascii format and prints them onto the terminal window.
|
||||
|
||||
Reference in New Issue
Block a user