Compare commits
19 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d05a757c5e | |
|
|
fbf87d33d5 | |
|
|
a6a4120adb | |
|
|
b229f94116 | |
|
|
17a7ef5c49 | |
|
|
62789959c8 | |
|
|
63f31c675e | |
|
|
66a0777ae8 | |
|
|
82c26c541a | |
|
|
088dd05e30 | |
|
|
2cc0d5f40d | |
|
|
f31ac047ea | |
|
|
39918a97fb | |
|
|
eebac8a229 | |
|
|
518011b970 | |
|
|
3c6a1a589f | |
|
|
df5eafe9a3 | |
|
|
dd8f65207e | |
|
|
f75ab428e5 |
|
|
@ -30,7 +30,6 @@ builds:
|
||||||
# - mips64
|
# - mips64
|
||||||
# - mips64le
|
# - mips64le
|
||||||
|
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
-
|
-
|
||||||
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
|
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
|
||||||
|
|
|
||||||
45
README.md
|
|
@ -28,6 +28,7 @@ Input formats currently supported:
|
||||||
* [Debian / Ubuntu-based](#debian-or-ubuntu-based-distros)
|
* [Debian / Ubuntu-based](#debian-or-ubuntu-based-distros)
|
||||||
* [Homebrew](#homebrew)
|
* [Homebrew](#homebrew)
|
||||||
* [AUR](#aur)
|
* [AUR](#aur)
|
||||||
|
* [Scoop](#scoop)
|
||||||
* [Snap](#snap)
|
* [Snap](#snap)
|
||||||
* [Go](#go)
|
* [Go](#go)
|
||||||
* [Linux (binaries)](#linux)
|
* [Linux (binaries)](#linux)
|
||||||
|
|
@ -41,7 +42,7 @@ Input formats currently supported:
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Debian or Ubuntu-based Distros
|
### Debian or Ubuntu-based Distros
|
||||||
|
|
||||||
Execute the following commands in order:
|
Execute the following commands in order:
|
||||||
|
|
||||||
|
|
@ -59,7 +60,7 @@ sudo apt install -y ascii-image-converter
|
||||||
To remove the package source (which means you won't be getting any further updates), execute this command:
|
To remove the package source (which means you won't be getting any further updates), execute this command:
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo rm -rfv /etc/apt/sources.list.d/ascii-image-converter.list
|
sudo rm -v /etc/apt/sources.list.d/ascii-image-converter.list
|
||||||
```
|
```
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
@ -92,6 +93,15 @@ AUR helper:
|
||||||
```
|
```
|
||||||
<aur-helper> -S ascii-image-converter-git
|
<aur-helper> -S ascii-image-converter-git
|
||||||
```
|
```
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
### Scoop
|
||||||
|
|
||||||
|
The scoop manifest is maintained by [brian6932](https://github.com/brian6932)
|
||||||
|
|
||||||
|
```
|
||||||
|
scoop install ascii-image-converter
|
||||||
|
```
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
|
@ -158,13 +168,19 @@ Example:
|
||||||
ascii-image-converter myImage.jpeg
|
ascii-image-converter myImage.jpeg
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note:** Piped binary input is also supported
|
||||||
|
> ```
|
||||||
|
> cat myImage.png | ascii-image-converter -
|
||||||
|
> ```
|
||||||
|
|
||||||
|
|
||||||
### Flags
|
### Flags
|
||||||
|
|
||||||
#### --color OR -C
|
#### --color OR -C
|
||||||
|
|
||||||
> **Note:** Your terminal must support 24-bit colors for appropriate results
|
> **Note:** Your terminal must support 24-bit or 8-bit colors for appropriate results. If 24-bit colors aren't supported, 8-bit color escape codes will be used
|
||||||
|
|
||||||
Display ascii art with the colors from original image. Works with the --negative flag as well.
|
Display ascii art with the colors from original image.
|
||||||
|
|
||||||
```
|
```
|
||||||
ascii-image-converter [image paths/urls] -C
|
ascii-image-converter [image paths/urls] -C
|
||||||
|
|
@ -391,10 +407,10 @@ Saves the passed GIF as an ascii art GIF with the name `<image-name>-ascii-art.g
|
||||||
|
|
||||||
> **Note:** This flag will be ignored if `--save-img` or `--save-gif` flags are not set
|
> **Note:** This flag will be ignored if `--save-img` or `--save-gif` flags are not set
|
||||||
|
|
||||||
This flag takes an RGB value that sets the background color in saved png and gif files.
|
This flag takes an RGBA value that sets the background color in saved png and gif files. The fourth value (alpha value) is the measure of background opacity ranging between 0 and 100.
|
||||||
|
|
||||||
```
|
```
|
||||||
ascii-image-converter [image paths/urls] -s . --save-bg 255,255,255 # For white background
|
ascii-image-converter [image paths/urls] -s . --save-bg 255,255,255,100 # For white background
|
||||||
```
|
```
|
||||||
|
|
||||||
#### --font
|
#### --font
|
||||||
|
|
@ -415,6 +431,14 @@ This flag takes an RGB value that sets the font color in saved png and gif files
|
||||||
ascii-image-converter [image paths/urls] -s . --font-color 0,0,0 # For black font color
|
ascii-image-converter [image paths/urls] -s . --font-color 0,0,0 # For black font color
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### --only-save
|
||||||
|
|
||||||
|
Don't print ascii art on the terminal if some saving flag is passed.
|
||||||
|
|
||||||
|
```
|
||||||
|
ascii-image-converter [image paths/urls] -s . --only-save
|
||||||
|
```
|
||||||
|
|
||||||
#### --formats
|
#### --formats
|
||||||
|
|
||||||
Display supported input formats.
|
Display supported input formats.
|
||||||
|
|
@ -460,11 +484,10 @@ func main() {
|
||||||
flags.SaveImagePath = "."
|
flags.SaveImagePath = "."
|
||||||
flags.CustomMap = " .-=+#@"
|
flags.CustomMap = " .-=+#@"
|
||||||
flags.FontFilePath = "./RobotoMono-Regular.ttf" // If file is in current directory
|
flags.FontFilePath = "./RobotoMono-Regular.ttf" // If file is in current directory
|
||||||
flags.SaveBackgroundColor = [3]int{50, 50, 50}
|
flags.SaveBackgroundColor = [4]int{50, 50, 50, 100}
|
||||||
|
|
||||||
// This MUST be set to true for environments where a terminal isn't available (such as web servers)
|
// Note: For environments where a terminal isn't available (such as web servers), you MUST
|
||||||
// However, for this, one of flags.Width, flags.Height or flags.Dimensions must be set.
|
// specify atleast one of flags.Width, flags.Height or flags.Dimensions
|
||||||
flags.NoTermSizeComparison = true
|
|
||||||
|
|
||||||
// Conversion for an image
|
// Conversion for an image
|
||||||
asciiArt, err := aic_package.Convert(filePath, flags)
|
asciiArt, err := aic_package.Convert(filePath, flags)
|
||||||
|
|
@ -523,8 +546,6 @@ You can fork the project and implement any changes you want for a pull request.
|
||||||
|
|
||||||
[github.com/gookit/color](https://github.com/gookit/color)
|
[github.com/gookit/color](https://github.com/gookit/color)
|
||||||
|
|
||||||
[github.com/asaskevich/govalidator](https://github.com/asaskevich/govalidator)
|
|
||||||
|
|
||||||
[github.com/makeworld-the-better-one/dither](https://github.com/makeworld-the-better-one/dither)
|
[github.com/makeworld-the-better-one/dither](https://github.com/makeworld-the-better-one/dither)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
||||||
|
|
@ -45,20 +45,26 @@ as an ascii art gif.
|
||||||
|
|
||||||
Multi-threading has been implemented in multiple places due to long execution time
|
Multi-threading has been implemented in multiple places due to long execution time
|
||||||
*/
|
*/
|
||||||
func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, localGif *os.File) error {
|
func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes, pipedInputBytes []byte, localGif *os.File) error {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
originalGif *gif.GIF
|
originalGif *gif.GIF
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if pathIsURl {
|
if gifPath == "-" {
|
||||||
|
originalGif, err = gif.DecodeAll(bytes.NewReader(pipedInputBytes))
|
||||||
|
} else if pathIsURl {
|
||||||
originalGif, err = gif.DecodeAll(bytes.NewReader(urlImgBytes))
|
originalGif, err = gif.DecodeAll(bytes.NewReader(urlImgBytes))
|
||||||
} else {
|
} else {
|
||||||
originalGif, err = gif.DecodeAll(localGif)
|
originalGif, err = gif.DecodeAll(localGif)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't decode %v: %v", gifPath, err)
|
if gifPath == "-" {
|
||||||
|
return fmt.Errorf("can't decode piped input: %v", err)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("can't decode %v: %v", gifPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -91,24 +97,33 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
|
||||||
// If a frame is found that is smaller than the first frame, then this gif contains smaller subimages that are
|
// If a frame is found that is smaller than the first frame, then this gif contains smaller subimages that are
|
||||||
// positioned inside the original gif. This behavior isn't supported by this app
|
// positioned inside the original gif. This behavior isn't supported by this app
|
||||||
if firstGifFrameWidth != frameImage.Bounds().Dx() || firstGifFrameHeight != frameImage.Bounds().Dy() {
|
if firstGifFrameWidth != frameImage.Bounds().Dx() || firstGifFrameHeight != frameImage.Bounds().Dy() {
|
||||||
fmt.Println("Error: GIF contains subimages smaller than default width and height\nProcess aborted because ascii-image-converter doesn't support subimage placement and transparency in GIFs\n")
|
if urlImgName == "" {
|
||||||
|
fmt.Printf("Error: " + gifPath + " contains subimages smaller than default width and height\n\nProcess aborted because ascii-image-converter doesn't support subimage placement and transparency in GIFs\n\n")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Error: " + urlImgName + " contains subimages smaller than default width and height\n\nProcess aborted because ascii-image-converter doesn't support subimage placement and transparency in GIFs\n\n")
|
||||||
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var imgSet [][]imgManip.AsciiPixel
|
var imgSet [][]imgManip.AsciiPixel
|
||||||
|
|
||||||
imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, width, height, flipX, flipY, full, braille, dither, noTermSizeComparison)
|
imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, width, height, flipX, flipY, full, braille, dither)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error:", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var asciiCharSet [][]imgManip.AsciiChar
|
var asciiCharSet [][]imgManip.AsciiChar
|
||||||
if braille {
|
if braille {
|
||||||
asciiCharSet = imgManip.ConvertToBrailleChars(imgSet, negative, colored, colorBg, fontColor, threshold)
|
asciiCharSet, err = imgManip.ConvertToBrailleChars(imgSet, negative, colored, grayscale, colorBg, fontColor, threshold)
|
||||||
} else {
|
} else {
|
||||||
asciiCharSet = imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, colorBg, customMap, fontColor)
|
asciiCharSet, err = imgManip.ConvertToAsciiChars(imgSet, negative, colored, grayscale, complex, colorBg, customMap, fontColor)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
gifFramesSlice[i].asciiCharSet = asciiCharSet
|
gifFramesSlice[i].asciiCharSet = asciiCharSet
|
||||||
gifFramesSlice[i].delay = originalGif.Delay[i]
|
gifFramesSlice[i].delay = originalGif.Delay[i]
|
||||||
|
|
||||||
|
|
@ -187,7 +202,7 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
|
||||||
colored || grayscale,
|
colored || grayscale,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error:", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,25 +246,29 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
|
||||||
gif.EncodeAll(gifFile, outGif)
|
gif.EncodeAll(gifFile, outGif)
|
||||||
|
|
||||||
fmt.Printf(" \r")
|
fmt.Printf(" \r")
|
||||||
|
|
||||||
|
fmt.Println("Saved " + fullPathName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display the gif
|
// Display the gif
|
||||||
loopCount := 0
|
if !onlySave {
|
||||||
for {
|
loopCount := 0
|
||||||
for i, asciiFrame := range asciiArtSet {
|
for {
|
||||||
clearScreen()
|
for i, asciiFrame := range asciiArtSet {
|
||||||
fmt.Println(asciiFrame)
|
clearScreen()
|
||||||
time.Sleep(time.Duration((time.Second * time.Duration(originalGif.Delay[i])) / 100))
|
fmt.Println(asciiFrame)
|
||||||
}
|
time.Sleep(time.Duration((time.Second * time.Duration(originalGif.Delay[i])) / 100))
|
||||||
|
}
|
||||||
|
|
||||||
// If gif is infinite loop
|
// If gif is infinite loop
|
||||||
if originalGif.LoopCount == 0 {
|
if originalGif.LoopCount == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
loopCount++
|
loopCount++
|
||||||
if loopCount == originalGif.LoopCount {
|
if loopCount == originalGif.LoopCount {
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,23 +27,29 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// This function decodes the passed image and returns an ascii art string, optionaly saving it as a .txt and/or .png file
|
// This function decodes the passed image and returns an ascii art string, optionaly saving it as a .txt and/or .png file
|
||||||
func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byte, localImg *os.File) (string, error) {
|
func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes, pipedInputBytes []byte, localImg *os.File) (string, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
imData image.Image
|
imData image.Image
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if pathIsURl {
|
if imagePath == "-" {
|
||||||
|
imData, _, err = image.Decode(bytes.NewReader(pipedInputBytes))
|
||||||
|
} else if pathIsURl {
|
||||||
imData, _, err = image.Decode(bytes.NewReader(urlImgBytes))
|
imData, _, err = image.Decode(bytes.NewReader(urlImgBytes))
|
||||||
} else {
|
} else {
|
||||||
imData, _, err = image.Decode(localImg)
|
imData, _, err = image.Decode(localImg)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("can't decode %v: %v", imagePath, err)
|
if imagePath == "-" {
|
||||||
|
return "", fmt.Errorf("can't decode piped input: %v", err)
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("can't decode %v: %v", imagePath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, width, height, flipX, flipY, full, braille, dither, noTermSizeComparison)
|
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, width, height, flipX, flipY, full, braille, dither)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -51,9 +57,12 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
|
||||||
var asciiSet [][]imgManip.AsciiChar
|
var asciiSet [][]imgManip.AsciiChar
|
||||||
|
|
||||||
if braille {
|
if braille {
|
||||||
asciiSet = imgManip.ConvertToBrailleChars(imgSet, negative, colored, colorBg, fontColor, threshold)
|
asciiSet, err = imgManip.ConvertToBrailleChars(imgSet, negative, colored, grayscale, colorBg, fontColor, threshold)
|
||||||
} else {
|
} else {
|
||||||
asciiSet = imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, colorBg, customMap, fontColor)
|
asciiSet, err = imgManip.ConvertToAsciiChars(imgSet, negative, colored, grayscale, complex, colorBg, customMap, fontColor)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save ascii art as .png image before printing it, if --save-img flag is passed
|
// Save ascii art as .png image before printing it, if --save-img flag is passed
|
||||||
|
|
@ -64,6 +73,7 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
|
||||||
saveImagePath,
|
saveImagePath,
|
||||||
imagePath,
|
imagePath,
|
||||||
urlImgName,
|
urlImgName,
|
||||||
|
onlySave,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|
||||||
return "", fmt.Errorf("can't save file: %v", err)
|
return "", fmt.Errorf("can't save file: %v", err)
|
||||||
|
|
@ -77,6 +87,7 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
|
||||||
imagePath,
|
imagePath,
|
||||||
saveTxtPath,
|
saveTxtPath,
|
||||||
urlImgName,
|
urlImgName,
|
||||||
|
onlySave,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|
||||||
return "", fmt.Errorf("can't save file: %v", err)
|
return "", fmt.Errorf("can't save file: %v", err)
|
||||||
|
|
@ -86,5 +97,8 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
|
||||||
ascii := flattenAscii(asciiSet, colored || grayscale, false)
|
ascii := flattenAscii(asciiSet, colored || grayscale, false)
|
||||||
result := strings.Join(ascii, "\n")
|
result := strings.Join(ascii, "\n")
|
||||||
|
|
||||||
|
if onlySave {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,32 +35,40 @@ import (
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var pipedInputTypes = []string{
|
||||||
|
"image/png",
|
||||||
|
"image/jpeg",
|
||||||
|
"image/webp",
|
||||||
|
"image/tiff",
|
||||||
|
"image/bmp",
|
||||||
|
}
|
||||||
|
|
||||||
// Return default configuration for flags.
|
// Return default configuration for flags.
|
||||||
// Can be sent directly to ConvertImage() for default ascii art
|
// Can be sent directly to ConvertImage() for default ascii art
|
||||||
func DefaultFlags() Flags {
|
func DefaultFlags() Flags {
|
||||||
return Flags{
|
return Flags{
|
||||||
Complex: false,
|
Complex: false,
|
||||||
Dimensions: nil,
|
Dimensions: nil,
|
||||||
Width: 0,
|
Width: 0,
|
||||||
Height: 0,
|
Height: 0,
|
||||||
SaveTxtPath: "",
|
SaveTxtPath: "",
|
||||||
SaveImagePath: "",
|
SaveImagePath: "",
|
||||||
SaveGifPath: "",
|
SaveGifPath: "",
|
||||||
Negative: false,
|
Negative: false,
|
||||||
Colored: false,
|
Colored: false,
|
||||||
CharBackgroundColor: false,
|
CharBackgroundColor: false,
|
||||||
Grayscale: false,
|
Grayscale: false,
|
||||||
CustomMap: "",
|
CustomMap: "",
|
||||||
FlipX: false,
|
FlipX: false,
|
||||||
FlipY: false,
|
FlipY: false,
|
||||||
Full: false,
|
Full: false,
|
||||||
FontFilePath: "",
|
FontFilePath: "",
|
||||||
FontColor: [3]int{255, 255, 255},
|
FontColor: [3]int{255, 255, 255},
|
||||||
SaveBackgroundColor: [3]int{0, 0, 0},
|
SaveBackgroundColor: [4]int{0, 0, 0, 100},
|
||||||
Braille: false,
|
Braille: false,
|
||||||
Threshold: 128,
|
Threshold: 128,
|
||||||
Dither: false,
|
Dither: false,
|
||||||
NoTermSizeComparison: false,
|
OnlySave: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,44 +104,84 @@ func Convert(filePath string, flags Flags) (string, error) {
|
||||||
braille = flags.Braille
|
braille = flags.Braille
|
||||||
threshold = flags.Threshold
|
threshold = flags.Threshold
|
||||||
dither = flags.Dither
|
dither = flags.Dither
|
||||||
noTermSizeComparison = flags.NoTermSizeComparison
|
onlySave = flags.OnlySave
|
||||||
|
|
||||||
|
inputIsGif = path.Ext(filePath) == ".gif"
|
||||||
|
|
||||||
// Declared at the start since some variables are initially used in conditional blocks
|
// Declared at the start since some variables are initially used in conditional blocks
|
||||||
var (
|
var (
|
||||||
localFile *os.File
|
localFile *os.File
|
||||||
urlImgBytes []byte
|
urlImgBytes []byte
|
||||||
urlImgName string = ""
|
urlImgName string = ""
|
||||||
err error
|
pipedInputBytes []byte
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
pathIsURl := isURL(filePath)
|
pathIsURl := isURL(filePath)
|
||||||
|
|
||||||
// Different modes of reading data depending upon whether or not filePath is a url
|
// Different modes of reading data depending upon whether or not filePath is a url
|
||||||
if pathIsURl {
|
|
||||||
fmt.Printf("Fetching file from url...\r")
|
|
||||||
|
|
||||||
retrievedImage, err := http.Get(filePath)
|
if filePath != "-" {
|
||||||
if err != nil {
|
if pathIsURl {
|
||||||
return "", fmt.Errorf("can't fetch content: %v", err)
|
fmt.Printf("Fetching file from url...\r")
|
||||||
|
|
||||||
|
retrievedImage, err := http.Get(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("can't fetch content: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
urlImgBytes, err = ioutil.ReadAll(retrievedImage.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read fetched content: %v", err)
|
||||||
|
}
|
||||||
|
defer retrievedImage.Body.Close()
|
||||||
|
|
||||||
|
urlImgName = path.Base(filePath)
|
||||||
|
fmt.Printf(" \r") // To erase "Fetching image from url..." text from terminal
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
localFile, err = os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to open file: %v", err)
|
||||||
|
}
|
||||||
|
defer localFile.Close()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
urlImgBytes, err = ioutil.ReadAll(retrievedImage.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to read fetched content: %v", err)
|
|
||||||
}
|
|
||||||
defer retrievedImage.Body.Close()
|
|
||||||
|
|
||||||
urlImgName = path.Base(filePath)
|
|
||||||
fmt.Printf(" \r") // To erase "Fetching image from url..." text from terminal
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Check file/data type of piped input
|
||||||
|
|
||||||
localFile, err = os.Open(filePath)
|
if !isInputFromPipe() {
|
||||||
if err != nil {
|
return "", fmt.Errorf("there is no input being piped to stdin")
|
||||||
return "", fmt.Errorf("unable to open file: %v", err)
|
|
||||||
}
|
}
|
||||||
defer localFile.Close()
|
|
||||||
|
|
||||||
|
pipedInputBytes, err = ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to read piped input: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileType := http.DetectContentType(pipedInputBytes)
|
||||||
|
invalidInput := true
|
||||||
|
|
||||||
|
if fileType == "image/gif" {
|
||||||
|
inputIsGif = true
|
||||||
|
invalidInput = false
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for _, inputType := range pipedInputTypes {
|
||||||
|
if fileType == inputType {
|
||||||
|
invalidInput = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not sure if I should uncomment this.
|
||||||
|
// The output may be piped to another program and a warning would contaminate that
|
||||||
|
if invalidInput {
|
||||||
|
// fmt.Println("Warning: file type of piped input could not be determined, treating it as an image")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If path to font file is provided, use it
|
// If path to font file is provided, use it
|
||||||
|
|
@ -151,9 +199,9 @@ func Convert(filePath string, flags Flags) (string, error) {
|
||||||
tempFont, _ = truetype.Parse(embeddedDejaVuObliqueFont)
|
tempFont, _ = truetype.Parse(embeddedDejaVuObliqueFont)
|
||||||
}
|
}
|
||||||
|
|
||||||
if path.Ext(filePath) == ".gif" {
|
if inputIsGif {
|
||||||
return "", pathIsGif(filePath, urlImgName, pathIsURl, urlImgBytes, localFile)
|
return "", pathIsGif(filePath, urlImgName, pathIsURl, urlImgBytes, pipedInputBytes, localFile)
|
||||||
} else {
|
} else {
|
||||||
return pathIsImage(filePath, urlImgName, pathIsURl, urlImgBytes, localFile)
|
return pathIsImage(filePath, urlImgName, pathIsURl, urlImgBytes, pipedInputBytes, localFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package aic_package
|
package aic_package
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
|
@ -48,7 +49,7 @@ images will considerably decrease ascii art quality because of smaller font size
|
||||||
|
|
||||||
Size of resulting image may also be considerably larger than original image.
|
Size of resulting image may also be considerably larger than original image.
|
||||||
*/
|
*/
|
||||||
func createImageToSave(asciiArt [][]imgManip.AsciiChar, colored bool, saveImagePath, imagePath, urlImgName string) error {
|
func createImageToSave(asciiArt [][]imgManip.AsciiChar, colored bool, saveImagePath, imagePath, urlImgName string, onlySave bool) error {
|
||||||
|
|
||||||
constant := 14.0
|
constant := 14.0
|
||||||
|
|
||||||
|
|
@ -73,10 +74,11 @@ func createImageToSave(asciiArt [][]imgManip.AsciiChar, colored bool, saveImageP
|
||||||
dc := gg.NewContext(imgWidth, imgHeight)
|
dc := gg.NewContext(imgWidth, imgHeight)
|
||||||
|
|
||||||
// Set image background
|
// Set image background
|
||||||
dc.SetRGB(
|
dc.SetRGBA(
|
||||||
float64(saveBgColor[0])/255,
|
float64(saveBgColor[0])/255,
|
||||||
float64(saveBgColor[1])/255,
|
float64(saveBgColor[1])/255,
|
||||||
float64(saveBgColor[2])/255,
|
float64(saveBgColor[2])/255,
|
||||||
|
float64(saveBgColor[3])/100,
|
||||||
)
|
)
|
||||||
dc.Clear()
|
dc.Clear()
|
||||||
|
|
||||||
|
|
@ -138,5 +140,9 @@ func createImageToSave(asciiArt [][]imgManip.AsciiChar, colored bool, saveImageP
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if onlySave {
|
||||||
|
fmt.Println("Saved " + fullPathName)
|
||||||
|
}
|
||||||
|
|
||||||
return dc.SavePNG(fullPathName)
|
return dc.SavePNG(fullPathName)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
imgManip "github.com/TheZoraiz/ascii-image-converter/image_manipulation"
|
imgManip "github.com/TheZoraiz/ascii-image-converter/image_manipulation"
|
||||||
)
|
)
|
||||||
|
|
||||||
func saveAsciiArt(asciiSet [][]imgManip.AsciiChar, imagePath, savePath, urlImgName string) error {
|
func saveAsciiArt(asciiSet [][]imgManip.AsciiChar, imagePath, savePath, urlImgName string, onlySave bool) error {
|
||||||
// To make sure uncolored ascii art is the one saved as .txt
|
// To make sure uncolored ascii art is the one saved as .txt
|
||||||
saveAscii := flattenAscii(asciiSet, false, true)
|
saveAscii := flattenAscii(asciiSet, false, true)
|
||||||
|
|
||||||
|
|
@ -46,7 +46,13 @@ func saveAsciiArt(asciiSet [][]imgManip.AsciiChar, imagePath, savePath, urlImgNa
|
||||||
|
|
||||||
// If path exists
|
// If path exists
|
||||||
if _, err := os.Stat(savePath); !os.IsNotExist(err) {
|
if _, err := os.Stat(savePath); !os.IsNotExist(err) {
|
||||||
return ioutil.WriteFile(savePath+saveFileName, []byte(strings.Join(saveAscii, "\n")), 0666)
|
err := ioutil.WriteFile(savePath+saveFileName, []byte(strings.Join(saveAscii, "\n")), 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if onlySave {
|
||||||
|
fmt.Println("Saved " + savePath + saveFileName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("save path %v does not exist", savePath)
|
return fmt.Errorf("save path %v does not exist", savePath)
|
||||||
}
|
}
|
||||||
|
|
@ -61,6 +67,13 @@ func createSaveFileName(imagePath, urlImgName, label string) (string, error) {
|
||||||
return newName + label, nil
|
return newName + label, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if imagePath == "-" {
|
||||||
|
if inputIsGif {
|
||||||
|
return "piped-gif" + label, nil
|
||||||
|
}
|
||||||
|
return "piped-img" + label, nil
|
||||||
|
}
|
||||||
|
|
||||||
fileInfo, err := os.Stat(imagePath)
|
fileInfo, err := os.Stat(imagePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -120,7 +133,7 @@ func getFullSavePath(imageName, saveFilePath string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isURL(urlString string) bool {
|
func isURL(urlString string) bool {
|
||||||
if len(urlString) < 7 {
|
if len(urlString) < 8 {
|
||||||
return false
|
return false
|
||||||
} else if urlString[:7] == "http://" || urlString[:8] == "https://" {
|
} else if urlString[:7] == "http://" || urlString[:8] == "https://" {
|
||||||
return true
|
return true
|
||||||
|
|
@ -155,3 +168,8 @@ func clearScreen() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isInputFromPipe() bool {
|
||||||
|
fileInfo, _ := os.Stdin.Stat()
|
||||||
|
return fileInfo.Mode()&os.ModeCharDevice == 0
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ type Flags struct {
|
||||||
|
|
||||||
// Background RGB color in saved png or gif files.
|
// Background RGB color in saved png or gif files.
|
||||||
// This will be ignored if Flags.SaveImagePath or Flags.SaveGifPath are not set
|
// This will be ignored if Flags.SaveImagePath or Flags.SaveGifPath are not set
|
||||||
SaveBackgroundColor [3]int
|
SaveBackgroundColor [4]int
|
||||||
|
|
||||||
// Use braille characters instead of ascii. Terminal must support UTF-8 encoding.
|
// Use braille characters instead of ascii. Terminal must support UTF-8 encoding.
|
||||||
// Otherwise, problems may be encountered with colored or even uncolored braille art.
|
// Otherwise, problems may be encountered with colored or even uncolored braille art.
|
||||||
|
|
@ -99,36 +99,33 @@ type Flags struct {
|
||||||
// is meant for braille art. Therefore, it will be ignored if Flags.Braille is false
|
// is meant for braille art. Therefore, it will be ignored if Flags.Braille is false
|
||||||
Dither bool
|
Dither bool
|
||||||
|
|
||||||
// Set this to true to disable comparing ascii art size to terminal. However, at least
|
// If Flags.SaveImagePath, Flags.SaveTxtPath or Flags.SaveGifPath are set, then don't
|
||||||
// one of Flags.Width, Flags.Height or Flags.Dimensions should be passed to keep it from
|
// print on terminal
|
||||||
// throwing an error.
|
OnlySave bool
|
||||||
//
|
|
||||||
// Note: This option is added for using the library in an environment without terminals (such as web servers).
|
|
||||||
// Furthermore, coloring options will not work outside of a terminal environment.
|
|
||||||
NoTermSizeComparison bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dimensions []int
|
dimensions []int
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
complex bool
|
complex bool
|
||||||
saveTxtPath string
|
saveTxtPath string
|
||||||
saveImagePath string
|
saveImagePath string
|
||||||
saveGifPath string
|
saveGifPath string
|
||||||
grayscale bool
|
grayscale bool
|
||||||
negative bool
|
negative bool
|
||||||
colored bool
|
colored bool
|
||||||
colorBg bool
|
colorBg bool
|
||||||
customMap string
|
customMap string
|
||||||
flipX bool
|
flipX bool
|
||||||
flipY bool
|
flipY bool
|
||||||
full bool
|
full bool
|
||||||
fontPath string
|
fontPath string
|
||||||
fontColor [3]int
|
fontColor [3]int
|
||||||
saveBgColor [3]int
|
saveBgColor [4]int
|
||||||
braille bool
|
braille bool
|
||||||
threshold int
|
threshold int
|
||||||
dither bool
|
dither bool
|
||||||
noTermSizeComparison bool
|
onlySave bool
|
||||||
|
inputIsGif bool
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build unix, !windows
|
//go:build (unix && ignore) || !windows
|
||||||
|
// +build unix,ignore !windows
|
||||||
|
|
||||||
package winsize
|
package winsize
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build windows, !unix
|
//go:build (windows && ignore) || !unix
|
||||||
|
// +build windows,ignore !unix
|
||||||
|
|
||||||
package winsize
|
package winsize
|
||||||
|
|
||||||
|
|
|
||||||
94
cmd/root.go
|
|
@ -52,12 +52,13 @@ var (
|
||||||
braille bool
|
braille bool
|
||||||
threshold int
|
threshold int
|
||||||
dither bool
|
dither bool
|
||||||
|
onlySave bool
|
||||||
|
|
||||||
// Root commands
|
// Root commands
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "ascii-image-converter [image paths/urls]",
|
Use: "ascii-image-converter [image paths/urls or piped stdin]",
|
||||||
Short: "Converts images and gifs into ascii art",
|
Short: "Converts images and gifs into ascii art",
|
||||||
Version: "1.9.2",
|
Version: "1.13.1",
|
||||||
Long: "This tool converts images into ascii art and prints them on the terminal.\nFurther configuration can be managed with flags.",
|
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
|
// Not RunE since help text is getting larger and seeing it for every error impacts user experience
|
||||||
|
|
@ -68,50 +69,64 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := aic_package.Flags{
|
flags := aic_package.Flags{
|
||||||
Complex: complex,
|
Complex: complex,
|
||||||
Dimensions: dimensions,
|
Dimensions: dimensions,
|
||||||
Width: width,
|
Width: width,
|
||||||
Height: height,
|
Height: height,
|
||||||
SaveTxtPath: saveTxtPath,
|
SaveTxtPath: saveTxtPath,
|
||||||
SaveImagePath: saveImagePath,
|
SaveImagePath: saveImagePath,
|
||||||
SaveGifPath: saveGifPath,
|
SaveGifPath: saveGifPath,
|
||||||
Negative: negative,
|
Negative: negative,
|
||||||
Colored: colored,
|
Colored: colored,
|
||||||
CharBackgroundColor: colorBg,
|
CharBackgroundColor: colorBg,
|
||||||
Grayscale: grayscale,
|
Grayscale: grayscale,
|
||||||
CustomMap: customMap,
|
CustomMap: customMap,
|
||||||
FlipX: flipX,
|
FlipX: flipX,
|
||||||
FlipY: flipY,
|
FlipY: flipY,
|
||||||
Full: full,
|
Full: full,
|
||||||
FontFilePath: fontFile,
|
FontFilePath: fontFile,
|
||||||
FontColor: [3]int{fontColor[0], fontColor[1], fontColor[2]},
|
FontColor: [3]int{fontColor[0], fontColor[1], fontColor[2]},
|
||||||
SaveBackgroundColor: [3]int{saveBgColor[0], saveBgColor[1], saveBgColor[2]},
|
SaveBackgroundColor: [4]int{saveBgColor[0], saveBgColor[1], saveBgColor[2], saveBgColor[3]},
|
||||||
Braille: braille,
|
Braille: braille,
|
||||||
Threshold: threshold,
|
Threshold: threshold,
|
||||||
Dither: dither,
|
Dither: dither,
|
||||||
NoTermSizeComparison: false,
|
OnlySave: onlySave,
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[0] == "-" {
|
||||||
|
printAscii(args[0], flags)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, imagePath := range args {
|
for _, imagePath := range args {
|
||||||
|
if err := printAscii(imagePath, flags); err != nil {
|
||||||
if asciiArt, err := aic_package.Convert(imagePath, flags); err == nil {
|
return
|
||||||
fmt.Printf("%s", asciiArt)
|
|
||||||
} else {
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func printAscii(imagePath string, flags aic_package.Flags) error {
|
||||||
|
|
||||||
|
if asciiArt, err := aic_package.Convert(imagePath, flags); err == nil {
|
||||||
|
fmt.Printf("%s", asciiArt)
|
||||||
|
} else {
|
||||||
|
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 err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !onlySave {
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Cobra configuration from here on
|
// Cobra configuration from here on
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
|
|
@ -128,7 +143,7 @@ func init() {
|
||||||
rootCmd.Flags().SortFlags = false
|
rootCmd.Flags().SortFlags = false
|
||||||
|
|
||||||
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ascii-image-converter.yaml)")
|
// 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 original colors\nTerminal must support 24-bit colors\n(Inverts with --negative flag)\n(Overrides --grayscale and --font-color flags)\n")
|
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with original colors\nIf 24-bit colors aren't supported, uses 8-bit\n(Inverts with --negative flag)\n(Overrides --grayscale and --font-color flags)\n")
|
||||||
rootCmd.PersistentFlags().BoolVar(&colorBg, "color-bg", false, "If some color flag is passed, use that color\non character background instead of foreground\n(Inverts with --negative flag)\n(Only applicable for terminal display)\n")
|
rootCmd.PersistentFlags().BoolVar(&colorBg, "color-bg", false, "If some color flag is passed, use that color\non character background instead of foreground\n(Inverts with --negative flag)\n(Only applicable for terminal display)\n")
|
||||||
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length\ne.g. -d 60,30 (defaults to terminal height)\n(Overrides --width and --height flags)\n")
|
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length\ne.g. -d 60,30 (defaults to terminal height)\n(Overrides --width and --height flags)\n")
|
||||||
rootCmd.PersistentFlags().IntVarP(&width, "width", "W", 0, "Set width for ascii art in CHARACTER length\nHeight is kept to aspect ratio\ne.g. -W 60\n")
|
rootCmd.PersistentFlags().IntVarP(&width, "width", "W", 0, "Set width for ascii art in CHARACTER length\nHeight is kept to aspect ratio\ne.g. -W 60\n")
|
||||||
|
|
@ -146,9 +161,10 @@ func init() {
|
||||||
rootCmd.PersistentFlags().StringVarP(&saveImagePath, "save-img", "s", "", "Save ascii art as a .png file\nFormat: <image-name>-ascii-art.png\nImage will be saved in passed path\n(pass . for current directory)\n")
|
rootCmd.PersistentFlags().StringVarP(&saveImagePath, "save-img", "s", "", "Save ascii art as 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 as a .txt file\nFormat: <image-name>-ascii-art.txt\nFile will be saved in passed path\n(pass . for current directory)\n")
|
rootCmd.PersistentFlags().StringVar(&saveTxtPath, "save-txt", "", "Save ascii art as a .txt file\nFormat: <image-name>-ascii-art.txt\nFile will be saved in passed path\n(pass . for current directory)\n")
|
||||||
rootCmd.PersistentFlags().StringVar(&saveGifPath, "save-gif", "", "If input is a gif, save it as a .gif file\nFormat: <gif-name>-ascii-art.gif\nGif will be saved in passed path\n(pass . for current directory)\n")
|
rootCmd.PersistentFlags().StringVar(&saveGifPath, "save-gif", "", "If input is a gif, save it as a .gif file\nFormat: <gif-name>-ascii-art.gif\nGif will be saved in passed path\n(pass . for current directory)\n")
|
||||||
rootCmd.PersistentFlags().IntSliceVar(&saveBgColor, "save-bg", nil, "Set background color for --save-img\nand --save-gif flags\nPass an RGB value\ne.g. --save-bg 255,255,255\n(Defaults to 0,0,0)\n")
|
rootCmd.PersistentFlags().IntSliceVar(&saveBgColor, "save-bg", nil, "Set background color for --save-img\nand --save-gif flags\nPass an RGBA value\ne.g. --save-bg 255,255,255,100\n(Defaults to 0,0,0,100)\n")
|
||||||
rootCmd.PersistentFlags().StringVar(&fontFile, "font", "", "Set font for --save-img and --save-gif flags\nPass file path to font .ttf file\ne.g. --font ./RobotoMono-Regular.ttf\n(Defaults to Hack-Regular for ascii and\n DejaVuSans-Oblique for braille)\n")
|
rootCmd.PersistentFlags().StringVar(&fontFile, "font", "", "Set font for --save-img and --save-gif flags\nPass file path to font .ttf file\ne.g. --font ./RobotoMono-Regular.ttf\n(Defaults to Hack-Regular for ascii and\n DejaVuSans-Oblique for braille)\n")
|
||||||
rootCmd.PersistentFlags().IntSliceVar(&fontColor, "font-color", nil, "Set font color for terminal as well as\n--save-img and --save-gif flags\nPass an RGB value\ne.g. --font-color 0,0,0\n(Defaults to 255,255,255)\n")
|
rootCmd.PersistentFlags().IntSliceVar(&fontColor, "font-color", nil, "Set font color for terminal as well as\n--save-img and --save-gif flags\nPass an RGB value\ne.g. --font-color 0,0,0\n(Defaults to 255,255,255)\n")
|
||||||
|
rootCmd.PersistentFlags().BoolVar(&onlySave, "only-save", false, "Don't print ascii art on terminal\nif some saving flag is passed\n")
|
||||||
rootCmd.PersistentFlags().BoolVar(&formatsTrue, "formats", false, "Display supported input formats\n")
|
rootCmd.PersistentFlags().BoolVar(&formatsTrue, "formats", false, "Display supported input formats\n")
|
||||||
|
|
||||||
rootCmd.PersistentFlags().BoolP("help", "h", false, "Help for "+rootCmd.Name()+"\n")
|
rootCmd.PersistentFlags().BoolP("help", "h", false, "Help for "+rootCmd.Name()+"\n")
|
||||||
|
|
|
||||||
66
cmd/util.go
|
|
@ -19,8 +19,6 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/TheZoraiz/ascii-image-converter/aic_package/winsize"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check input and flag values for detecting errors or invalid inputs
|
// Check input and flag values for detecting errors or invalid inputs
|
||||||
|
|
@ -29,6 +27,8 @@ func checkInputAndFlags(args []string) bool {
|
||||||
gifCount := 0
|
gifCount := 0
|
||||||
gifPresent := false
|
gifPresent := false
|
||||||
nonGifPresent := false
|
nonGifPresent := false
|
||||||
|
pipeCharPresent := false
|
||||||
|
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
extension := path.Ext(arg)
|
extension := path.Ext(arg)
|
||||||
|
|
||||||
|
|
@ -38,14 +38,18 @@ func checkInputAndFlags(args []string) bool {
|
||||||
} else {
|
} else {
|
||||||
nonGifPresent = true
|
nonGifPresent = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if arg == "-" {
|
||||||
|
pipeCharPresent = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if gifPresent && nonGifPresent {
|
if gifPresent && nonGifPresent && !onlySave {
|
||||||
fmt.Printf("Error: There are other inputs along with GIFs\nDue to the potential looping nature of GIFs, non-GIFs must not be supplied alongside\n\n")
|
fmt.Printf("Error: There are other inputs along with GIFs\nDue to the potential looping nature of GIFs, non-GIFs must not be supplied alongside\n\n")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if gifCount > 1 {
|
if gifCount > 1 && !onlySave {
|
||||||
fmt.Printf("Error: There are multiple GIFs supplied\nDue to the potential looping nature of GIFs, only one GIF per command is supported\n\n")
|
fmt.Printf("Error: There are multiple GIFs supplied\nDue to the potential looping nature of GIFs, only one GIF per command is supported\n\n")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +66,12 @@ func checkInputAndFlags(args []string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
fmt.Printf("Error: Need at least 1 input path/url\nUse the -h flag for more info\n\n")
|
fmt.Printf("Error: Need at least 1 input path/url or piped input\nUse the -h flag for more info\n\n")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 1 && pipeCharPresent {
|
||||||
|
fmt.Printf("Error: You cannot pass in piped input alongside other inputs\n\n")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,18 +92,6 @@ func checkInputAndFlags(args []string) bool {
|
||||||
fmt.Printf("Error: invalid values for dimensions\n\n")
|
fmt.Printf("Error: invalid values for dimensions\n\n")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultTermWidth, _, err := winsize.GetTerminalSize()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultTermWidth -= 1
|
|
||||||
if dimensions[0] > defaultTermWidth {
|
|
||||||
fmt.Printf("Error: set width must be lower than terminal width\n\n")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if width != 0 || height != 0 {
|
if width != 0 || height != 0 {
|
||||||
|
|
@ -102,21 +99,9 @@ func checkInputAndFlags(args []string) bool {
|
||||||
if width != 0 && height != 0 {
|
if width != 0 && height != 0 {
|
||||||
fmt.Printf("Error: both --width and --height can't be set. Use --dimensions instead\n\n")
|
fmt.Printf("Error: both --width and --height can't be set. Use --dimensions instead\n\n")
|
||||||
return true
|
return true
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
defaultTermWidth, _, err := winsize.GetTerminalSize()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if set width exceeds terminal
|
|
||||||
defaultTermWidth -= 1
|
|
||||||
if width > defaultTermWidth {
|
|
||||||
fmt.Printf("Error: set width must be lower than terminal width\n\n")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if width < 0 {
|
if width < 0 {
|
||||||
fmt.Printf("Error: invalid value for width\n\n")
|
fmt.Printf("Error: invalid value for width\n\n")
|
||||||
return true
|
return true
|
||||||
|
|
@ -132,21 +117,23 @@ func checkInputAndFlags(args []string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if saveBgColor == nil {
|
if saveBgColor == nil {
|
||||||
saveBgColor = []int{0, 0, 0}
|
saveBgColor = []int{0, 0, 0, 100}
|
||||||
} else {
|
} else {
|
||||||
bgValues := len(saveBgColor)
|
bgValues := len(saveBgColor)
|
||||||
if bgValues != 3 {
|
if bgValues != 4 {
|
||||||
fmt.Printf("Error: --save-bg requires 3 values for RGB, got %v\n\n", bgValues)
|
fmt.Printf("Error: --save-bg requires 4 values for RGBA, got %v\n\n", bgValues)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if saveBgColor[0] < 0 || saveBgColor[1] < 0 || saveBgColor[2] < 0 {
|
if saveBgColor[0] < 0 || saveBgColor[1] < 0 || saveBgColor[2] < 0 || saveBgColor[3] < 0 {
|
||||||
fmt.Printf("Error: RBG values must be between 0 and 255\n\n")
|
fmt.Printf("Error: RBG values must be between 0 and 255\n")
|
||||||
|
fmt.Printf("Error: Opacity value must be between 0 and 100\n\n")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if saveBgColor[0] > 255 || saveBgColor[1] > 255 || saveBgColor[2] > 255 {
|
if saveBgColor[0] > 255 || saveBgColor[1] > 255 || saveBgColor[2] > 255 || saveBgColor[3] > 100 {
|
||||||
fmt.Printf("Error: RBG values must be between 0 and 255\n\n")
|
fmt.Printf("Error: RBG values must be between 0 and 255\n")
|
||||||
|
fmt.Printf("Error: Opacity value must be between 0 and 100\n\n")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -185,5 +172,10 @@ func checkInputAndFlags(args []string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (saveTxtPath == "" && saveImagePath == "" && saveGifPath == "") && onlySave {
|
||||||
|
fmt.Printf("Error: you need to supply one of --save-img, --save-txt or --save-gif for using --only-save\n\n")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 582 KiB After Width: | Height: | Size: 355 KiB |
|
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 352 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 669 KiB After Width: | Height: | Size: 409 KiB |
|
Before Width: | Height: | Size: 4.9 MiB After Width: | Height: | Size: 3.0 MiB |
25
go.mod
|
|
@ -1,26 +1,35 @@
|
||||||
module github.com/TheZoraiz/ascii-image-converter
|
module github.com/TheZoraiz/ascii-image-converter
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/fogleman/gg v1.3.0
|
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/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
github.com/gookit/color v1.4.2
|
github.com/gookit/color v1.4.2
|
||||||
github.com/magiconair/properties v1.8.5 // indirect
|
|
||||||
github.com/makeworld-the-better-one/dither/v2 v2.2.0
|
github.com/makeworld-the-better-one/dither/v2 v2.2.0
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
|
||||||
github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d
|
github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d
|
||||||
|
github.com/spf13/cobra v1.1.3
|
||||||
|
github.com/spf13/viper v1.7.1
|
||||||
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.1 // indirect
|
github.com/pelletier/go-toml v1.9.1 // indirect
|
||||||
github.com/spf13/afero v1.6.0 // indirect
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
github.com/spf13/cast v1.3.1 // indirect
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
github.com/spf13/cobra v1.1.3
|
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b // 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
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
2
go.sum
|
|
@ -19,8 +19,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
|
|
||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
package image_conversions
|
package image_conversions
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/gookit/color"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
||||||
asciiTableSimple = " .:-=+*#%@"
|
asciiTableSimple = " .:-=+*#%@"
|
||||||
|
|
@ -55,7 +49,7 @@ to a 2D image_conversions.AsciiChar slice
|
||||||
If complex parameter is true, values are compared to 70 levels of color density in ASCII characters.
|
If complex parameter is true, values are compared to 70 levels of color density in ASCII characters.
|
||||||
Otherwise, values are compared to 10 levels of color density in ASCII characters.
|
Otherwise, values are compared to 10 levels of color density in ASCII characters.
|
||||||
*/
|
*/
|
||||||
func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex, colorBg bool, customMap string, fontColor [3]int) [][]AsciiChar {
|
func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, grayscale, complex, colorBg bool, customMap string, fontColor [3]int) ([][]AsciiChar, error) {
|
||||||
|
|
||||||
height := len(imgSet)
|
height := len(imgSet)
|
||||||
width := len(imgSet[0])
|
width := len(imgSet[0])
|
||||||
|
|
@ -128,29 +122,34 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex, colo
|
||||||
tempInt = (len(chosenTable) - 1) - tempInt
|
tempInt = (len(chosenTable) - 1) - tempInt
|
||||||
}
|
}
|
||||||
|
|
||||||
rStr := strconv.Itoa(r)
|
|
||||||
gStr := strconv.Itoa(g)
|
|
||||||
bStr := strconv.Itoa(b)
|
|
||||||
|
|
||||||
var char AsciiChar
|
var char AsciiChar
|
||||||
|
|
||||||
char.Simple = chosenTable[tempInt]
|
asciiChar := chosenTable[tempInt]
|
||||||
|
char.Simple = asciiChar
|
||||||
|
|
||||||
|
var err error
|
||||||
if colorBg {
|
if colorBg {
|
||||||
char.OriginalColor = color.Sprintf("<bg="+rStr+","+gStr+","+bStr+">%v</>", chosenTable[tempInt])
|
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), asciiChar, true)
|
||||||
} else {
|
} else {
|
||||||
char.OriginalColor = color.Sprintf("<fg="+rStr+","+gStr+","+bStr+">%v</>", chosenTable[tempInt])
|
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), asciiChar, false)
|
||||||
|
}
|
||||||
|
if (colored || grayscale) && err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If font color is not set, use a simple string. Otherwise, use True color
|
// If font color is not set, use a simple string. Otherwise, use True color
|
||||||
if fontColor != [3]int{255, 255, 255} {
|
if fontColor != [3]int{255, 255, 255} {
|
||||||
fcR := strconv.Itoa(fontColor[0])
|
fcR := fontColor[0]
|
||||||
fcG := strconv.Itoa(fontColor[1])
|
fcG := fontColor[1]
|
||||||
fcB := strconv.Itoa(fontColor[2])
|
fcB := fontColor[2]
|
||||||
|
|
||||||
if colorBg {
|
if colorBg {
|
||||||
char.SetColor = color.Sprintf("<bg="+fcR+","+fcG+","+fcB+">%v</>", chosenTable[tempInt])
|
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), asciiChar, true)
|
||||||
} else {
|
} else {
|
||||||
char.SetColor = color.Sprintf("<fg="+fcR+","+fcG+","+fcB+">%v</>", chosenTable[tempInt])
|
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), asciiChar, false)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +164,7 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex, colo
|
||||||
result = append(result, tempSlice)
|
result = append(result, tempSlice)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -174,7 +173,7 @@ to a 2D image_conversions.AsciiChar slice
|
||||||
|
|
||||||
Unlike ConvertToAsciiChars(), this function calculates braille characters instead of ascii
|
Unlike ConvertToAsciiChars(), this function calculates braille characters instead of ascii
|
||||||
*/
|
*/
|
||||||
func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, colorBg bool, fontColor [3]int, threshold int) [][]AsciiChar {
|
func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, grayscale, colorBg bool, fontColor [3]int, threshold int) ([][]AsciiChar, error) {
|
||||||
|
|
||||||
BrailleThreshold = uint32(threshold)
|
BrailleThreshold = uint32(threshold)
|
||||||
|
|
||||||
|
|
@ -216,29 +215,33 @@ func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, colorBg boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rStr := strconv.Itoa(r)
|
|
||||||
gStr := strconv.Itoa(g)
|
|
||||||
bStr := strconv.Itoa(b)
|
|
||||||
|
|
||||||
var char AsciiChar
|
var char AsciiChar
|
||||||
|
|
||||||
char.Simple = brailleChar
|
char.Simple = brailleChar
|
||||||
|
|
||||||
|
var err error
|
||||||
if colorBg {
|
if colorBg {
|
||||||
char.OriginalColor = color.Sprintf("<bg="+rStr+","+gStr+","+bStr+">%v</>", brailleChar)
|
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), brailleChar, true)
|
||||||
} else {
|
} else {
|
||||||
char.OriginalColor = color.Sprintf("<fg="+rStr+","+gStr+","+bStr+">%v</>", brailleChar)
|
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), brailleChar, false)
|
||||||
|
}
|
||||||
|
if (colored || grayscale) && err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If font color is not set, use a simple string. Otherwise, use True color
|
// If font color is not set, use a simple string. Otherwise, use True color
|
||||||
if fontColor != [3]int{255, 255, 255} {
|
if fontColor != [3]int{255, 255, 255} {
|
||||||
fcR := strconv.Itoa(fontColor[0])
|
fcR := fontColor[0]
|
||||||
fcG := strconv.Itoa(fontColor[1])
|
fcG := fontColor[1]
|
||||||
fcB := strconv.Itoa(fontColor[2])
|
fcB := fontColor[2]
|
||||||
|
|
||||||
if colorBg {
|
if colorBg {
|
||||||
char.SetColor = color.Sprintf("<bg="+fcR+","+fcG+","+fcB+">%v</>", brailleChar)
|
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), brailleChar, true)
|
||||||
} else {
|
} else {
|
||||||
char.SetColor = color.Sprintf("<fg="+fcR+","+fcG+","+fcB+">%v</>", brailleChar)
|
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), brailleChar, false)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,7 +257,7 @@ func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, colorBg boo
|
||||||
result = append(result, tempSlice)
|
result = append(result, tempSlice)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through the BrailleStruct table to see which dots need to be highlighted
|
// Iterate through the BrailleStruct table to see which dots need to be highlighted
|
||||||
|
|
|
||||||
|
|
@ -34,16 +34,10 @@ getting numeric data for ASCII character comparison.
|
||||||
|
|
||||||
The returned 2D AsciiPixel slice contains each corresponding pixel's values
|
The returned 2D AsciiPixel slice contains each corresponding pixel's values
|
||||||
*/
|
*/
|
||||||
func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full, isBraille, dither, noTermSizeComparison bool) ([][]AsciiPixel, error) {
|
func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full, isBraille, dither bool) ([][]AsciiPixel, error) {
|
||||||
|
|
||||||
var smallImg image.Image
|
smallImg, err := resizeImage(img, full, isBraille, dimensions, width, height)
|
||||||
var err error
|
|
||||||
|
|
||||||
if noTermSizeComparison {
|
|
||||||
smallImg, err = resizeImageNoTerm(img, isBraille, dimensions, width, height)
|
|
||||||
} else {
|
|
||||||
smallImg, err = resizeImage(img, full, isBraille, dimensions, width, height)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/TheZoraiz/ascii-image-converter/aic_package/winsize"
|
"github.com/TheZoraiz/ascii-image-converter/aic_package/winsize"
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
|
gookitColor "github.com/gookit/color"
|
||||||
"github.com/makeworld-the-better-one/dither/v2"
|
"github.com/makeworld-the-better-one/dither/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -44,16 +45,16 @@ func resizeImage(img image.Image, full, isBraille bool, dimensions []int, width,
|
||||||
var asciiWidth, asciiHeight int
|
var asciiWidth, asciiHeight int
|
||||||
var smallImg image.Image
|
var smallImg image.Image
|
||||||
|
|
||||||
terminalWidth, terminalHeight, err := winsize.GetTerminalSize()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
imgWidth := float64(img.Bounds().Dx())
|
imgWidth := float64(img.Bounds().Dx())
|
||||||
imgHeight := float64(img.Bounds().Dy())
|
imgHeight := float64(img.Bounds().Dy())
|
||||||
aspectRatio := imgWidth / imgHeight
|
aspectRatio := imgWidth / imgHeight
|
||||||
|
|
||||||
if full {
|
if full {
|
||||||
|
terminalWidth, _, err := winsize.GetTerminalSize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
asciiWidth = terminalWidth - 1
|
asciiWidth = terminalWidth - 1
|
||||||
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
||||||
asciiHeight = int(0.5 * float64(asciiHeight))
|
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||||
|
|
@ -61,10 +62,6 @@ func resizeImage(img image.Image, full, isBraille bool, dimensions []int, width,
|
||||||
} else if (width != 0 || height != 0) && len(dimensions) == 0 {
|
} else if (width != 0 || height != 0) && len(dimensions) == 0 {
|
||||||
// If either width or height is set and dimensions aren't given
|
// If either width or height is set and dimensions aren't given
|
||||||
|
|
||||||
if width > terminalWidth-1 {
|
|
||||||
return nil, fmt.Errorf("set width must be lower than terminal width")
|
|
||||||
}
|
|
||||||
|
|
||||||
if width != 0 && height == 0 {
|
if width != 0 && height == 0 {
|
||||||
// If width is set and height is not set, use width to calculate aspect ratio
|
// If width is set and height is not set, use width to calculate aspect ratio
|
||||||
|
|
||||||
|
|
@ -87,17 +84,18 @@ func resizeImage(img image.Image, full, isBraille bool, dimensions []int, width,
|
||||||
asciiWidth = 1
|
asciiWidth = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if asciiWidth > terminalWidth-1 {
|
|
||||||
return nil, fmt.Errorf("width calculated with aspect ratio exceeds terminal width")
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("both width and height can't be set. Use dimensions instead")
|
return nil, fmt.Errorf("error: both width and height can't be set. Use dimensions instead")
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if len(dimensions) == 0 {
|
} else if len(dimensions) == 0 {
|
||||||
// This condition calculates aspect ratio according to terminal height
|
// This condition calculates aspect ratio according to terminal height
|
||||||
|
|
||||||
|
terminalWidth, terminalHeight, err := winsize.GetTerminalSize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
asciiHeight = terminalHeight - 1
|
asciiHeight = terminalHeight - 1
|
||||||
asciiWidth = int(float64(asciiHeight) * aspectRatio)
|
asciiWidth = int(float64(asciiHeight) * aspectRatio)
|
||||||
asciiWidth = int(2 * float64(asciiWidth))
|
asciiWidth = int(2 * float64(asciiWidth))
|
||||||
|
|
@ -110,69 +108,13 @@ func resizeImage(img image.Image, full, isBraille bool, dimensions []int, width,
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Else, set passed dimensions
|
||||||
|
|
||||||
asciiWidth = dimensions[0]
|
asciiWidth = dimensions[0]
|
||||||
asciiHeight = dimensions[1]
|
asciiHeight = dimensions[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeated despite being in cmd/root.go to maintain support for library
|
// Because one braille character has 8 dots (4 rows and 2 columns)
|
||||||
//
|
|
||||||
// If there are passed dimensions, check whether the width exceeds terminal width
|
|
||||||
if len(dimensions) > 0 && !full {
|
|
||||||
if dimensions[0] > terminalWidth-1 {
|
|
||||||
return nil, fmt.Errorf("set width must be lower than terminal width")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isBraille {
|
|
||||||
asciiWidth *= 2
|
|
||||||
asciiHeight *= 4
|
|
||||||
}
|
|
||||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
|
||||||
|
|
||||||
return smallImg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeImageNoTerm(img image.Image, isBraille bool, dimensions []int, width, height int) (image.Image, error) {
|
|
||||||
|
|
||||||
var asciiWidth, asciiHeight int
|
|
||||||
var smallImg image.Image
|
|
||||||
|
|
||||||
imgWidth := float64(img.Bounds().Dx())
|
|
||||||
imgHeight := float64(img.Bounds().Dy())
|
|
||||||
aspectRatio := imgWidth / imgHeight
|
|
||||||
|
|
||||||
if (width != 0 || height != 0) && len(dimensions) == 0 {
|
|
||||||
if width != 0 && height == 0 {
|
|
||||||
|
|
||||||
asciiWidth = width
|
|
||||||
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
|
||||||
asciiHeight = int(0.5 * float64(asciiHeight))
|
|
||||||
|
|
||||||
if asciiHeight == 0 {
|
|
||||||
asciiHeight = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if height != 0 && width == 0 {
|
|
||||||
|
|
||||||
asciiHeight = height
|
|
||||||
asciiWidth = int(float64(asciiHeight) * aspectRatio)
|
|
||||||
asciiWidth = int(2 * float64(asciiWidth))
|
|
||||||
|
|
||||||
if asciiWidth == 0 {
|
|
||||||
asciiWidth = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("error: both width and height can't be set. Use dimensions instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if len(dimensions) != 0 {
|
|
||||||
asciiWidth = dimensions[0]
|
|
||||||
asciiHeight = dimensions[1]
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("error: at least one of width, height or dimensions should be passed for NoTermSizeComparison")
|
|
||||||
}
|
|
||||||
|
|
||||||
if isBraille {
|
if isBraille {
|
||||||
asciiWidth *= 2
|
asciiWidth *= 2
|
||||||
asciiHeight *= 4
|
asciiHeight *= 4
|
||||||
|
|
@ -200,3 +142,25 @@ func reverse(imgSet [][]AsciiPixel, flipX, flipY bool) [][]AsciiPixel {
|
||||||
|
|
||||||
return imgSet
|
return imgSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var termColorLevel string = gookitColor.TermColorLevel().String()
|
||||||
|
|
||||||
|
// This functions calculates terminal color level between rgb colors and 256-colors
|
||||||
|
// and returns the character with escape codes appropriately
|
||||||
|
func getColoredCharForTerm(r, g, b uint8, char string, background bool) (string, error) {
|
||||||
|
var coloredChar string
|
||||||
|
|
||||||
|
if termColorLevel == "millions" {
|
||||||
|
colorRenderer := gookitColor.RGB(uint8(r), uint8(g), uint8(b), background)
|
||||||
|
coloredChar = colorRenderer.Sprintf("%v", char)
|
||||||
|
|
||||||
|
} else if termColorLevel == "hundreds" {
|
||||||
|
colorRenderer := gookitColor.RGB(uint8(r), uint8(g), uint8(b), background).C256()
|
||||||
|
coloredChar = colorRenderer.Sprintf("%v", char)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("your terminal supports neither 24-bit nor 8-bit colors. Other coloring options aren't available")
|
||||||
|
}
|
||||||
|
|
||||||
|
return coloredChar, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: ascii-image-converter
|
name: ascii-image-converter
|
||||||
base: core18
|
base: core18
|
||||||
version: "1.9.2"
|
version: "1.13.1"
|
||||||
summary: Convert images and gifs into ascii art
|
summary: Convert images and gifs into ascii art
|
||||||
description: |
|
description: |
|
||||||
ascii-image-converter is a command-line tool that converts images into ascii art and prints
|
ascii-image-converter is a command-line tool that converts images into ascii art and prints
|
||||||
|
|
|
||||||