Compare commits
38 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d05a757c5e | |
|
|
fbf87d33d5 | |
|
|
a6a4120adb | |
|
|
b229f94116 | |
|
|
17a7ef5c49 | |
|
|
62789959c8 | |
|
|
63f31c675e | |
|
|
66a0777ae8 | |
|
|
82c26c541a | |
|
|
088dd05e30 | |
|
|
2cc0d5f40d | |
|
|
f31ac047ea | |
|
|
39918a97fb | |
|
|
eebac8a229 | |
|
|
518011b970 | |
|
|
3c6a1a589f | |
|
|
df5eafe9a3 | |
|
|
dd8f65207e | |
|
|
f75ab428e5 | |
|
|
6793039277 | |
|
|
178c48fbe1 | |
|
|
de35b19962 | |
|
|
2db0093e58 | |
|
|
8be1d4c9b5 | |
|
|
19f9581235 | |
|
|
7c0c89df66 | |
|
|
a8701af97a | |
|
|
e6e52d7aec | |
|
|
917e1a5f0e | |
|
|
202179dd2d | |
|
|
50b6b0081f | |
|
|
d7df581771 | |
|
|
26d910fc9d | |
|
|
6102f4dcaa | |
|
|
ed4e662781 | |
|
|
f318be9c56 | |
|
|
1f3cd6ad00 | |
|
|
f5ffd897ca |
|
|
@ -30,7 +30,6 @@ builds:
|
||||||
# - mips64
|
# - mips64
|
||||||
# - mips64le
|
# - mips64le
|
||||||
|
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
-
|
-
|
||||||
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
|
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
|
||||||
|
|
|
||||||
204
README.md
|
|
@ -1,12 +1,14 @@
|
||||||
# ascii-image-converter
|
# ascii-image-converter
|
||||||
|
|
||||||
[](https://github.com/TheZoraiz/ascii-image-converter/releases/latest)
|
[](https://github.com/TheZoraiz/ascii-image-converter/releases/latest)
|
||||||
[](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
|
[](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
|
||||||
[](https://golang.org/)
|
[](https://golang.org/)
|
||||||

|

|
||||||
[](https://snapcraft.io/ascii-image-converter)
|
[](https://snapcraft.io/ascii-image-converter)
|
||||||
|
|
||||||
ascii-image-converter is a command-line tool that converts images into ascii art and prints them out onto the console. Available on Windows, Linux and macOS. GIFs are now experimentally supported as well.
|
ascii-image-converter is a command-line tool that converts images into ascii art and prints them out onto the console. Available on Windows, Linux and macOS.
|
||||||
|
|
||||||
|
Now supports braille art!
|
||||||
|
|
||||||
Input formats currently supported:
|
Input formats currently supported:
|
||||||
* JPEG/JPG
|
* JPEG/JPG
|
||||||
|
|
@ -14,14 +16,20 @@ Input formats currently supported:
|
||||||
* BMP
|
* BMP
|
||||||
* WEBP
|
* WEBP
|
||||||
* TIFF/TIF
|
* TIFF/TIF
|
||||||
* GIF (Experimental)
|
* GIF
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/all.gif">
|
||||||
|
</p>
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
* [Debian / Ubuntu-based](#debian-or-ubuntu-based-distros)
|
* [Debian / Ubuntu-based](#debian-or-ubuntu-based-distros)
|
||||||
* [Homebrew](#homebrew)
|
* [Homebrew](#homebrew)
|
||||||
* [Snap](#brew)
|
* [AUR](#aur)
|
||||||
|
* [Scoop](#scoop)
|
||||||
|
* [Snap](#snap)
|
||||||
* [Go](#go)
|
* [Go](#go)
|
||||||
* [Linux (binaries)](#linux)
|
* [Linux (binaries)](#linux)
|
||||||
* [Windows (binaries)](#windows)
|
* [Windows (binaries)](#windows)
|
||||||
|
|
@ -34,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:
|
||||||
|
|
||||||
|
|
@ -52,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>
|
||||||
|
|
@ -67,6 +75,36 @@ brew install TheZoraiz/ascii-image-converter/ascii-image-converter
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
### AUR
|
||||||
|
|
||||||
|
The AUR repo is maintained by [magnus-tesshu](https://aur.archlinux.org/account/magnus-tesshu)
|
||||||
|
|
||||||
|
Standard way:
|
||||||
|
```
|
||||||
|
git clone https://aur.archlinux.org/ascii-image-converter-git.git
|
||||||
|
```
|
||||||
|
```
|
||||||
|
cd ascii-image-converter-git/
|
||||||
|
```
|
||||||
|
```
|
||||||
|
makepkg -si
|
||||||
|
```
|
||||||
|
AUR helper:
|
||||||
|
```
|
||||||
|
<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>
|
||||||
|
|
||||||
### Snap
|
### Snap
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,7 +122,6 @@ Visit [the app's snap store listing](https://snapcraft.io/ascii-image-converter)
|
||||||
|
|
||||||
### Go
|
### Go
|
||||||
|
|
||||||
For installing through Go
|
|
||||||
```
|
```
|
||||||
go install github.com/TheZoraiz/ascii-image-converter@latest
|
go install github.com/TheZoraiz/ascii-image-converter@latest
|
||||||
```
|
```
|
||||||
|
|
@ -108,8 +145,7 @@ Now you can use ascii-image-converter in the terminal. Execute `ascii-image-conv
|
||||||
You will need to set an Environment Variable to the folder the ascii-image-converter.exe executable is placed in to be able to use it in the command prompt. Follow the instructions in case of confusion:
|
You will need to set an Environment Variable to the folder the ascii-image-converter.exe executable is placed in to be able to use it in the command prompt. Follow the instructions in case of confusion:
|
||||||
|
|
||||||
Download the archive for your Windows architecture [here](https://github.com/TheZoraiz/ascii-image-converter/releases/latest), extract it, and open the extracted folder. Now, copy the folder path from the top of the file explorer and follow these instructions:
|
Download the archive for your Windows architecture [here](https://github.com/TheZoraiz/ascii-image-converter/releases/latest), extract it, and open the extracted folder. Now, copy the folder path from the top of the file explorer and follow these instructions:
|
||||||
* In Search, search for and then select: System (Control Panel)
|
* In Search, search for and then select: Advanced System Settings
|
||||||
* Click the Advanced System settings link.
|
|
||||||
* Click Environment Variables. In the section User Variables find the Path environment variable and select it. Click "Edit".
|
* Click Environment Variables. In the section User Variables find the Path environment variable and select it. Click "Edit".
|
||||||
* In the Edit Environment Variable window, click "New" and then paste the path of the folder that you copied initially.
|
* In the Edit Environment Variable window, click "New" and then paste the path of the folder that you copied initially.
|
||||||
* Click "Ok" on all open windows.
|
* Click "Ok" on all open windows.
|
||||||
|
|
@ -131,30 +167,20 @@ Example:
|
||||||
```
|
```
|
||||||
ascii-image-converter myImage.jpeg
|
ascii-image-converter myImage.jpeg
|
||||||
```
|
```
|
||||||
<br>
|
|
||||||
Single image:
|
|
||||||
|
|
||||||
<p align="center">
|
> **Note:** Piped binary input is also supported
|
||||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/base.gif">
|
> ```
|
||||||
</p>
|
> cat myImage.png | ascii-image-converter -
|
||||||
|
> ```
|
||||||
|
|
||||||
Multiple images:
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/all.gif">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
GIF:
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/gif-example.gif">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
### Flags
|
### Flags
|
||||||
|
|
||||||
#### --color OR -C
|
#### --color OR -C
|
||||||
|
|
||||||
Display ascii art with the colors from original image. Works with the --negative flag as well.
|
> **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.
|
||||||
|
|
||||||
```
|
```
|
||||||
ascii-image-converter [image paths/urls] -C
|
ascii-image-converter [image paths/urls] -C
|
||||||
|
|
@ -166,6 +192,50 @@ ascii-image-converter [image paths/urls] --color
|
||||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/color.gif">
|
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/color.gif">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
#### --braille OR -b
|
||||||
|
|
||||||
|
> **Note:** Braille pattern display heavily depends on which terminal or font you're using. In windows, try changing the font from command prompt properties if braille characters don't display
|
||||||
|
|
||||||
|
Use braille characters instead of ascii. For this flag, your terminal must support braille patters (UTF-8) properly. Otherwise, you may encounter problems with colored or even uncolored braille art.
|
||||||
|
```
|
||||||
|
ascii-image-converter [image paths/urls] -b
|
||||||
|
# Or
|
||||||
|
ascii-image-converter [image paths/urls] --braille
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/braille.gif">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
#### --threshold
|
||||||
|
|
||||||
|
Set threshold value to compare for braille art when converting each pixel into a dot. Value must be between 0 and 255.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
ascii-image-converter [image paths/urls] -b --threshold 170
|
||||||
|
```
|
||||||
|
|
||||||
|
#### --dither
|
||||||
|
|
||||||
|
Apply dithering on image to make braille art more visible. Since braille dots can only be on or off, dithering images makes them more visible in braille art.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
ascii-image-converter [image paths/urls] -b --dither
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/dither.gif">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
#### --color-bg
|
||||||
|
|
||||||
|
If any of the coloring flags is passed, this flag will transfer its color to each character's background. instead of foreground. However, this option isn't available for `--save-img` and `--save-gif`
|
||||||
|
```
|
||||||
|
ascii-image-converter [image paths/urls] -C --color-bg
|
||||||
|
```
|
||||||
|
|
||||||
#### --dimensions OR -d
|
#### --dimensions OR -d
|
||||||
|
|
||||||
> **Note:** Don't immediately append another flag with -d
|
> **Note:** Don't immediately append another flag with -d
|
||||||
|
|
@ -337,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
|
||||||
|
|
@ -361,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.
|
||||||
|
|
@ -373,14 +451,14 @@ ascii-image-converter --formats
|
||||||
|
|
||||||
## Library Usage
|
## Library Usage
|
||||||
|
|
||||||
> **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). Furthermore, GIF conversion is not advised as it isn't fully library-compatible yet.
|
> **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).
|
||||||
|
|
||||||
First, install the library with:
|
First, install the library with:
|
||||||
```
|
```
|
||||||
go get -u github.com/TheZoraiz/ascii-image-converter/aic_package
|
go get -u github.com/TheZoraiz/ascii-image-converter/aic_package
|
||||||
```
|
```
|
||||||
|
|
||||||
The library is to be used as follows:
|
For an image:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
@ -398,23 +476,18 @@ func main() {
|
||||||
flags := aic_package.DefaultFlags()
|
flags := aic_package.DefaultFlags()
|
||||||
|
|
||||||
// This part is optional.
|
// This part is optional.
|
||||||
// You can directly pass default flags variable to Convert() if you wish.
|
// You can directly pass default flags variable to aic_package.Convert() if you wish.
|
||||||
// For clarity, all flags are covered in this example, but you can use specific ones.
|
// There are more flags, but these are the ones shown for demonstration
|
||||||
flags.Complex = false // Use complex character set
|
flags.Dimensions = []int{50, 25}
|
||||||
flags.Dimensions = []int{50, 25} // 50 by 25 ascii art size
|
flags.Colored = true
|
||||||
flags.SaveTxtPath = "." // Save generated text in same directory
|
flags.SaveTxtPath = "."
|
||||||
flags.SaveImagePath = "." // Save generated PNG image in same directory
|
flags.SaveImagePath = "."
|
||||||
flags.SaveGifPath = "." // If gif is provided, save ascii art gif in same directory
|
flags.CustomMap = " .-=+#@"
|
||||||
flags.Negative = false // Ascii art will have negative color-depth
|
flags.FontFilePath = "./RobotoMono-Regular.ttf" // If file is in current directory
|
||||||
flags.Colored = true // Keep colors from original image. This overrides flags.Grayscale and flags.FontColor
|
flags.SaveBackgroundColor = [4]int{50, 50, 50, 100}
|
||||||
flags.Grayscale = false // Returns grayscale ascii art. This overrides flags.FontColor
|
|
||||||
flags.CustomMap = " .-=+#@" // Starting from darkest to brightest shades. This overrides flags.Complex
|
// Note: For environments where a terminal isn't available (such as web servers), you MUST
|
||||||
flags.FlipX = false // Flips ascii art horizontally
|
// specify atleast one of flags.Width, flags.Height or flags.Dimensions
|
||||||
flags.FlipY = false // Flips ascii art vertically
|
|
||||||
flags.Full = false // Display ascii art that fills the terminal width. This overrides flags.Dimensions
|
|
||||||
flags.FontFilePath = "./RobotoMono-Regular.ttf" // File path to font .ttf file for saved png and gif files. Ignored if flags.SaveImagePath or flags.SaveGifPath are not set
|
|
||||||
flags.FontColor = [3]int{0, 0, 0} // Font color for terminal and saved png and gif files.
|
|
||||||
flags.SaveBackgroundColor = [3]int{255, 255, 255} // Font color for saved png and gif files. Ignored if flags.SaveImagePath or flags.SaveGifPath are not set.
|
|
||||||
|
|
||||||
// Conversion for an image
|
// Conversion for an image
|
||||||
asciiArt, err := aic_package.Convert(filePath, flags)
|
asciiArt, err := aic_package.Convert(filePath, flags)
|
||||||
|
|
@ -423,15 +496,34 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%v\n", asciiArt)
|
fmt.Printf("%v\n", asciiArt)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
<br>
|
||||||
|
|
||||||
|
> **Note:** GIF conversion is not advised as the function may run infinitely, depending on the GIF. More work needs to be done on this to make it more library-compatible.
|
||||||
|
|
||||||
|
For a GIF:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/TheZoraiz/ascii-image-converter/aic_package"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
filePath = "myGif.gif"
|
||||||
|
|
||||||
|
flags := aic_package.DefaultFlags()
|
||||||
|
|
||||||
// GIF CONVERSION IS AN EXPERIMENTAL FEATURE
|
|
||||||
// For a gif. This function may run infinitely, depending on the gif
|
|
||||||
// Work needs to be done on gif conversion to be more library-compatible
|
|
||||||
_, err := aic_package.Convert(filePath, flags)
|
_, err := aic_package.Convert(filePath, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
@ -450,11 +542,11 @@ You can fork the project and implement any changes you want for a pull request.
|
||||||
|
|
||||||
[github.com/nathan-fiscaletti/consolesize-go](https://github.com/nathan-fiscaletti/consolesize-go)
|
[github.com/nathan-fiscaletti/consolesize-go](https://github.com/nathan-fiscaletti/consolesize-go)
|
||||||
|
|
||||||
[github.com/nfnt/resize](https://github.com/nfnt/resize)
|
[github.com/disintegration/imaging](https://github.com/disintegration/imaging)
|
||||||
|
|
||||||
[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)
|
||||||
|
|
||||||
## 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) (string, 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,19 +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)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
var asciiCharSet [][]imgManip.AsciiChar
|
||||||
|
if braille {
|
||||||
|
asciiCharSet, err = imgManip.ConvertToBrailleChars(imgSet, negative, colored, grayscale, colorBg, fontColor, threshold)
|
||||||
|
} else {
|
||||||
|
asciiCharSet, err = imgManip.ConvertToAsciiChars(imgSet, negative, colored, grayscale, complex, colorBg, customMap, fontColor)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
asciiCharSet := imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, customMap, fontColor)
|
|
||||||
gifFramesSlice[i].asciiCharSet = asciiCharSet
|
gifFramesSlice[i].asciiCharSet = asciiCharSet
|
||||||
gifFramesSlice[i].delay = originalGif.Delay[i]
|
gifFramesSlice[i].delay = originalGif.Delay[i]
|
||||||
|
|
||||||
|
|
@ -137,12 +157,12 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
|
||||||
|
|
||||||
saveFileName, err := createSaveFileName(gifPath, urlImgName, "-ascii-art.gif")
|
saveFileName, err := createSaveFileName(gifPath, urlImgName, "-ascii-art.gif")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fullPathName, err := getFullSavePath(saveFileName, saveGifPath)
|
fullPathName, err := getFullSavePath(saveFileName, saveGifPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("can't save file: %v", err)
|
return fmt.Errorf("can't save file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializing some constants for gif. Done outside loop to save execution
|
// Initializing some constants for gif. Done outside loop to save execution
|
||||||
|
|
@ -182,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,34 +239,38 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
|
||||||
|
|
||||||
gifFile, err := os.OpenFile(fullPathName, os.O_WRONLY|os.O_CREATE, 0666)
|
gifFile, err := os.OpenFile(fullPathName, os.O_WRONLY|os.O_CREATE, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("can't save file: %v", err)
|
return fmt.Errorf("can't save file: %v", err)
|
||||||
}
|
}
|
||||||
defer gifFile.Close()
|
defer gifFile.Close()
|
||||||
|
|
||||||
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,28 +27,43 @@ 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)
|
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, width, height, flipX, flipY, full, braille, dither)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
asciiSet := imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, customMap, fontColor)
|
var asciiSet [][]imgManip.AsciiChar
|
||||||
|
|
||||||
|
if braille {
|
||||||
|
asciiSet, err = imgManip.ConvertToBrailleChars(imgSet, negative, colored, grayscale, colorBg, fontColor, threshold)
|
||||||
|
} else {
|
||||||
|
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
|
||||||
if saveImagePath != "" {
|
if saveImagePath != "" {
|
||||||
|
|
@ -58,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)
|
||||||
|
|
@ -71,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)
|
||||||
|
|
@ -80,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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,17 @@ import (
|
||||||
_ "golang.org/x/image/tiff"
|
_ "golang.org/x/image/tiff"
|
||||||
_ "golang.org/x/image/webp"
|
_ "golang.org/x/image/webp"
|
||||||
|
|
||||||
"github.com/asaskevich/govalidator"
|
|
||||||
"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 {
|
||||||
|
|
@ -49,6 +56,7 @@ func DefaultFlags() Flags {
|
||||||
SaveGifPath: "",
|
SaveGifPath: "",
|
||||||
Negative: false,
|
Negative: false,
|
||||||
Colored: false,
|
Colored: false,
|
||||||
|
CharBackgroundColor: false,
|
||||||
Grayscale: false,
|
Grayscale: false,
|
||||||
CustomMap: "",
|
CustomMap: "",
|
||||||
FlipX: false,
|
FlipX: false,
|
||||||
|
|
@ -56,7 +64,11 @@ func DefaultFlags() Flags {
|
||||||
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,
|
||||||
|
Threshold: 128,
|
||||||
|
Dither: false,
|
||||||
|
OnlySave: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +92,7 @@ func Convert(filePath string, flags Flags) (string, error) {
|
||||||
saveGifPath = flags.SaveGifPath
|
saveGifPath = flags.SaveGifPath
|
||||||
negative = flags.Negative
|
negative = flags.Negative
|
||||||
colored = flags.Colored
|
colored = flags.Colored
|
||||||
|
colorBg = flags.CharBackgroundColor
|
||||||
grayscale = flags.Grayscale
|
grayscale = flags.Grayscale
|
||||||
customMap = flags.CustomMap
|
customMap = flags.CustomMap
|
||||||
flipX = flags.FlipX
|
flipX = flags.FlipX
|
||||||
|
|
@ -88,43 +101,87 @@ func Convert(filePath string, flags Flags) (string, error) {
|
||||||
fontPath = flags.FontFilePath
|
fontPath = flags.FontFilePath
|
||||||
fontColor = flags.FontColor
|
fontColor = flags.FontColor
|
||||||
saveBgColor = flags.SaveBackgroundColor
|
saveBgColor = flags.SaveBackgroundColor
|
||||||
|
braille = flags.Braille
|
||||||
|
threshold = flags.Threshold
|
||||||
|
dither = flags.Dither
|
||||||
|
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 := govalidator.IsRequestURL(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
|
||||||
|
|
@ -138,11 +195,13 @@ func Convert(filePath string, flags Flags) (string, error) {
|
||||||
if tempFont, err = truetype.Parse(fontFile); err != nil {
|
if tempFont, err = truetype.Parse(fontFile); err != nil {
|
||||||
return "", fmt.Errorf("unable to parse font file: %v", err)
|
return "", fmt.Errorf("unable to parse font file: %v", err)
|
||||||
}
|
}
|
||||||
|
} else if braille {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,6 @@ func createGifFrameToSave(asciiArt [][]imgManip.AsciiChar, img image.Image, colo
|
||||||
|
|
||||||
dc.DrawImage(tempImg, 0, 0)
|
dc.DrawImage(tempImg, 0, 0)
|
||||||
|
|
||||||
// Load embedded font
|
|
||||||
tempFont, err := truetype.Parse(embeddedFontFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Font size increased during assignment to become more visible. This will not affect image drawing
|
// Font size increased during assignment to become more visible. This will not affect image drawing
|
||||||
fontFace := truetype.NewFace(tempFont, &truetype.Options{Size: fontSize * 1.5})
|
fontFace := truetype.NewFace(tempFont, &truetype.Options{Size: fontSize * 1.5})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package aic_package
|
package aic_package
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
|
@ -28,13 +29,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed Hack-Regular.ttf
|
//go:embed Hack-Regular.ttf
|
||||||
var embeddedFontFile []byte
|
var embeddedHackRegularFont []byte
|
||||||
|
|
||||||
|
//go:embed DejaVuSans-Oblique.ttf
|
||||||
|
var embeddedDejaVuObliqueFont []byte
|
||||||
|
|
||||||
var tempFont *truetype.Font
|
var tempFont *truetype.Font
|
||||||
|
|
||||||
// Load embedded font
|
// Load embedded font
|
||||||
func init() {
|
func init() {
|
||||||
tempFont, _ = truetype.Parse(embeddedFontFile)
|
tempFont, _ = truetype.Parse(embeddedHackRegularFont)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -45,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
|
||||||
|
|
||||||
|
|
@ -70,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()
|
||||||
|
|
||||||
|
|
@ -135,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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
## Note
|
||||||
|
|
||||||
|
The font `DejaVuSans-Oblique.ttf` is used for saving braille art .png images since it supports unicode and gave the best results. `Hack-Regular.ttf` is used for saving normal ascii art .png images.
|
||||||
|
|
@ -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
|
||||||
|
|
@ -79,24 +92,24 @@ func flattenAscii(asciiSet [][]imgManip.AsciiChar, colored, toSaveTxt bool) []st
|
||||||
var ascii []string
|
var ascii []string
|
||||||
|
|
||||||
for _, line := range asciiSet {
|
for _, line := range asciiSet {
|
||||||
var tempAscii []string
|
var tempAscii string
|
||||||
|
|
||||||
for i := 0; i < len(line); i++ {
|
for _, char := range line {
|
||||||
if toSaveTxt {
|
if toSaveTxt {
|
||||||
tempAscii = append(tempAscii, line[i].Simple)
|
tempAscii += char.Simple
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if colored {
|
if colored {
|
||||||
tempAscii = append(tempAscii, line[i].OriginalColor)
|
tempAscii += char.OriginalColor
|
||||||
} else if fontColor != [3]int{255, 255, 255} {
|
} else if fontColor != [3]int{255, 255, 255} {
|
||||||
tempAscii = append(tempAscii, line[i].SetColor)
|
tempAscii += char.SetColor
|
||||||
} else {
|
} else {
|
||||||
tempAscii = append(tempAscii, line[i].Simple)
|
tempAscii += char.Simple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ascii = append(ascii, strings.Join(tempAscii, ""))
|
ascii = append(ascii, tempAscii)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ascii
|
return ascii
|
||||||
|
|
@ -119,6 +132,15 @@ func getFullSavePath(imageName, saveFilePath string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isURL(urlString string) bool {
|
||||||
|
if len(urlString) < 8 {
|
||||||
|
return false
|
||||||
|
} else if urlString[:7] == "http://" || urlString[:8] == "https://" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Following is for clearing screen when showing gif
|
// Following is for clearing screen when showing gif
|
||||||
var clear map[string]func()
|
var clear map[string]func()
|
||||||
|
|
||||||
|
|
@ -146,3 +168,8 @@ func clearScreen() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isInputFromPipe() bool {
|
||||||
|
fileInfo, _ := os.Stdin.Stat()
|
||||||
|
return fileInfo.Mode()&os.ModeCharDevice == 0
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,17 @@ type Flags struct {
|
||||||
// This overrides Flags.Grayscale and Flags.FontColor
|
// This overrides Flags.Grayscale and Flags.FontColor
|
||||||
Colored bool
|
Colored bool
|
||||||
|
|
||||||
|
// If Flags.Colored, Flags.Grayscale or Flags.FontColor is set, use that color
|
||||||
|
// on each character's background in the terminal
|
||||||
|
CharBackgroundColor bool
|
||||||
|
|
||||||
// Keep grayscale colors from the original image. This uses the True color
|
// Keep grayscale colors from the original image. This uses the True color
|
||||||
// codes for the terminal and will work on saved .png and .gif files as well
|
// codes for the terminal and will work on saved .png and .gif files as well
|
||||||
// This overrides Flags.FontColor
|
// This overrides Flags.FontColor
|
||||||
Grayscale bool
|
Grayscale bool
|
||||||
|
|
||||||
// Pass custom ascii art characters as a string.
|
// Pass custom ascii art characters as a string.
|
||||||
|
// e.g. " .-=+#@".
|
||||||
// This overrides Flags.Complex
|
// This overrides Flags.Complex
|
||||||
CustomMap string
|
CustomMap string
|
||||||
|
|
||||||
|
|
@ -73,13 +78,30 @@ type Flags struct {
|
||||||
// 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
|
||||||
FontFilePath string
|
FontFilePath string
|
||||||
|
|
||||||
// Font RGB color in saved png or gif files.
|
// Font RGB color for terminal display and saved png or gif files.
|
||||||
// This will be ignored if Flags.SaveImagePath or Flags.SaveGifPath are not set
|
|
||||||
FontColor [3]int
|
FontColor [3]int
|
||||||
|
|
||||||
// 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.
|
||||||
|
// Otherwise, problems may be encountered with colored or even uncolored braille art.
|
||||||
|
// This overrides Flags.Complex and Flags.CustomMap
|
||||||
|
Braille bool
|
||||||
|
|
||||||
|
// Threshold for braille art if Flags.Braille is set to true. Value provided must
|
||||||
|
// be between 0 and 255. Ideal value is 128.
|
||||||
|
// This will be ignored if Flags.Braille is not set
|
||||||
|
Threshold int
|
||||||
|
|
||||||
|
// Apply FloydSteinberg dithering on an image before ascii conversion. This option
|
||||||
|
// is meant for braille art. Therefore, it will be ignored if Flags.Braille is false
|
||||||
|
Dither bool
|
||||||
|
|
||||||
|
// If Flags.SaveImagePath, Flags.SaveTxtPath or Flags.SaveGifPath are set, then don't
|
||||||
|
// print on terminal
|
||||||
|
OnlySave bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -93,11 +115,17 @@ var (
|
||||||
grayscale bool
|
grayscale bool
|
||||||
negative bool
|
negative bool
|
||||||
colored bool
|
colored 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
|
||||||
|
threshold int
|
||||||
|
dither 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
|
||||||
|
|
||||||
|
|
|
||||||
71
cmd/root.go
|
|
@ -40,6 +40,7 @@ var (
|
||||||
negative bool
|
negative bool
|
||||||
formatsTrue bool
|
formatsTrue bool
|
||||||
colored bool
|
colored bool
|
||||||
|
colorBg bool
|
||||||
grayscale bool
|
grayscale bool
|
||||||
customMap string
|
customMap string
|
||||||
flipX bool
|
flipX bool
|
||||||
|
|
@ -48,12 +49,16 @@ var (
|
||||||
fontFile string
|
fontFile string
|
||||||
fontColor []int
|
fontColor []int
|
||||||
saveBgColor []int
|
saveBgColor []int
|
||||||
|
braille bool
|
||||||
|
threshold int
|
||||||
|
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.6.0",
|
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
|
||||||
|
|
@ -73,6 +78,7 @@ var (
|
||||||
SaveGifPath: saveGifPath,
|
SaveGifPath: saveGifPath,
|
||||||
Negative: negative,
|
Negative: negative,
|
||||||
Colored: colored,
|
Colored: colored,
|
||||||
|
CharBackgroundColor: colorBg,
|
||||||
Grayscale: grayscale,
|
Grayscale: grayscale,
|
||||||
CustomMap: customMap,
|
CustomMap: customMap,
|
||||||
FlipX: flipX,
|
FlipX: flipX,
|
||||||
|
|
@ -80,29 +86,47 @@ var (
|
||||||
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,
|
||||||
|
Threshold: threshold,
|
||||||
|
Dither: dither,
|
||||||
|
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() {
|
||||||
|
|
@ -119,12 +143,16 @@ 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\n(Can work with the --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().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")
|
||||||
rootCmd.PersistentFlags().IntVarP(&height, "height", "H", 0, "Set height for ascii art in CHARACTER length\nWidth is kept to aspect ratio\ne.g. -H 60\n")
|
rootCmd.PersistentFlags().IntVarP(&height, "height", "H", 0, "Set height for ascii art in CHARACTER length\nWidth is kept to aspect ratio\ne.g. -H 60\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(Overrides --complex flag)\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(Overrides --complex flag)\n")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&grayscale, "grayscale", "g", false, "Display grayscale ascii art\n(Can work with --negative flag)\n(Overrides --font-color flag)\n")
|
rootCmd.PersistentFlags().BoolVarP(&braille, "braille", "b", false, "Use braille characters instead of ascii\nTerminal must support braille patterns properly\n(Overrides --complex and --map flags)\n")
|
||||||
|
rootCmd.PersistentFlags().IntVar(&threshold, "threshold", 0, "Threshold for braille art\nValue between 0-255 is accepted\ne.g. --threshold 170\n(Defaults to 128)\n")
|
||||||
|
rootCmd.PersistentFlags().BoolVar(&dither, "dither", false, "Apply dithering on image for braille\nart conversion\n(Only applicable with --braille flag)\n(Negates --threshold flag)\n")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&grayscale, "grayscale", "g", false, "Display grayscale ascii art\n(Inverts with --negative flag)\n(Overrides --font-color flag)\n")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&complex, "complex", "c", false, "Display ascii characters in a larger range\nMay result in higher quality\n")
|
rootCmd.PersistentFlags().BoolVarP(&complex, "complex", "c", false, "Display ascii characters in a larger range\nMay result in higher quality\n")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&full, "full", "f", false, "Use largest dimensions for ascii art\nthat fill the terminal width\n(Overrides --dimensions, --width and --height flags)\n")
|
rootCmd.PersistentFlags().BoolVarP(&full, "full", "f", false, "Use largest dimensions for ascii art\nthat fill the terminal width\n(Overrides --dimensions, --width and --height flags)\n")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&negative, "negative", "n", false, "Display ascii art in negative colors\n")
|
rootCmd.PersistentFlags().BoolVarP(&negative, "negative", "n", false, "Display ascii art in negative colors\n")
|
||||||
|
|
@ -133,14 +161,17 @@ 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 and --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)\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")
|
||||||
rootCmd.PersistentFlags().BoolP("version", "v", false, "Version for "+rootCmd.Name())
|
rootCmd.PersistentFlags().BoolP("version", "v", false, "Version for "+rootCmd.Name())
|
||||||
|
|
||||||
|
rootCmd.SetVersionTemplate("{{printf \"v%s\" .Version}}\n")
|
||||||
|
|
||||||
defaultUsageTemplate := rootCmd.UsageTemplate()
|
defaultUsageTemplate := rootCmd.UsageTemplate()
|
||||||
rootCmd.SetUsageTemplate(defaultUsageTemplate + "\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" +
|
"Distributed under the Apache License Version 2.0 (Apache-2.0)\n" +
|
||||||
|
|
|
||||||
82
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
|
||||||
}
|
}
|
||||||
|
|
@ -57,12 +61,17 @@ func checkInputAndFlags(args []string) bool {
|
||||||
"WEBP\n" +
|
"WEBP\n" +
|
||||||
"BMP\n" +
|
"BMP\n" +
|
||||||
"TIFF/TIF\n" +
|
"TIFF/TIF\n" +
|
||||||
"GIF (Experimental)\n\n")
|
"GIF\n\n")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -171,5 +158,24 @@ func checkInputAndFlags(args []string) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if threshold == 0 {
|
||||||
|
threshold = 128
|
||||||
|
}
|
||||||
|
|
||||||
|
if threshold < 0 || threshold > 255 {
|
||||||
|
fmt.Printf("Error: threshold must be between 0 and 255\n\n")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if dither && !braille {
|
||||||
|
fmt.Printf("Error: image dithering is only reserved for --braille flag\n\n")
|
||||||
|
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: 1.2 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 160 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 |
|
After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 3.3 MiB |
|
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 |
|
Before Width: | Height: | Size: 49 KiB |
27
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/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/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/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
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-20210504121937-7319ad40d33e
|
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
|
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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
13
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=
|
||||||
|
|
@ -38,6 +36,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
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/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
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 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
|
|
@ -122,6 +122,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
|
github.com/makeworld-the-better-one/dither/v2 v2.2.0 h1:VTMAiyyO1YIO07fZwuLNZZasJgKUmvsIA48ze3ALHPQ=
|
||||||
|
github.com/makeworld-the-better-one/dither/v2 v2.2.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
|
@ -142,8 +144,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d h1:PQW4Aqovdqc9efHl9EVA+bhKmuZ4ME1HvSYYDvaDiK0=
|
github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d h1:PQW4Aqovdqc9efHl9EVA+bhKmuZ4ME1HvSYYDvaDiK0=
|
||||||
github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d/go.mod h1:cxIIfNMTwff8f/ZvRouvWYF6wOoO7nj99neWSx2q/Es=
|
github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d/go.mod h1:cxIIfNMTwff8f/ZvRouvWYF6wOoO7nj99neWSx2q/Es=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
|
@ -226,8 +226,9 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0
|
||||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e h1:PzJMNfFQx+QO9hrC1GwZ4BoPGeNGhfeQEgcQFArEjPk=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||||
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
|
|
||||||
|
|
@ -16,101 +16,24 @@ limitations under the License.
|
||||||
|
|
||||||
package image_conversions
|
package image_conversions
|
||||||
|
|
||||||
import (
|
var (
|
||||||
"strconv"
|
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
||||||
|
asciiTableSimple = " .:-=+*#%@"
|
||||||
|
asciiTableDetailed = " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"
|
||||||
|
|
||||||
"github.com/gookit/color"
|
// Structure for braille dots
|
||||||
|
BrailleStruct = [4][2]int{
|
||||||
|
{0x1, 0x8},
|
||||||
|
{0x2, 0x10},
|
||||||
|
{0x4, 0x20},
|
||||||
|
{0x40, 0x80},
|
||||||
|
}
|
||||||
|
|
||||||
|
BrailleThreshold uint32
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
|
||||||
var asciiTableSimple = map[int]string{
|
|
||||||
0: " ",
|
|
||||||
1: ".",
|
|
||||||
2: ":",
|
|
||||||
3: "-",
|
|
||||||
4: "=",
|
|
||||||
5: "+",
|
|
||||||
6: "*",
|
|
||||||
7: "#",
|
|
||||||
8: "%",
|
|
||||||
9: "@",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
|
||||||
var asciiTableDetailed = map[int]string{
|
|
||||||
0: " ",
|
|
||||||
1: ".",
|
|
||||||
2: "'",
|
|
||||||
3: "`",
|
|
||||||
4: "^",
|
|
||||||
5: "\"",
|
|
||||||
6: ",",
|
|
||||||
7: ":",
|
|
||||||
8: ";",
|
|
||||||
9: "I",
|
|
||||||
10: "l",
|
|
||||||
11: "!",
|
|
||||||
12: "i",
|
|
||||||
13: ">",
|
|
||||||
14: "<",
|
|
||||||
15: "~",
|
|
||||||
16: "+",
|
|
||||||
17: "_",
|
|
||||||
18: "-",
|
|
||||||
19: "?",
|
|
||||||
20: "]",
|
|
||||||
21: "[",
|
|
||||||
22: "}",
|
|
||||||
23: "{",
|
|
||||||
24: "1",
|
|
||||||
25: ")",
|
|
||||||
26: "(",
|
|
||||||
27: "|",
|
|
||||||
28: "/",
|
|
||||||
29: "t",
|
|
||||||
30: "f",
|
|
||||||
31: "j",
|
|
||||||
32: "r",
|
|
||||||
33: "x",
|
|
||||||
34: "n",
|
|
||||||
35: "u",
|
|
||||||
36: "v",
|
|
||||||
37: "c",
|
|
||||||
38: "z",
|
|
||||||
39: "X",
|
|
||||||
40: "Y",
|
|
||||||
41: "U",
|
|
||||||
42: "J",
|
|
||||||
43: "C",
|
|
||||||
44: "L",
|
|
||||||
45: "Q",
|
|
||||||
46: "0",
|
|
||||||
47: "O",
|
|
||||||
48: "Z",
|
|
||||||
49: "m",
|
|
||||||
50: "w",
|
|
||||||
51: "q",
|
|
||||||
52: "p",
|
|
||||||
53: "d",
|
|
||||||
54: "b",
|
|
||||||
55: "k",
|
|
||||||
56: "h",
|
|
||||||
57: "a",
|
|
||||||
58: "o",
|
|
||||||
59: "*",
|
|
||||||
60: "#",
|
|
||||||
61: "M",
|
|
||||||
62: "W",
|
|
||||||
63: "&",
|
|
||||||
64: "8",
|
|
||||||
65: "%",
|
|
||||||
66: "B",
|
|
||||||
67: "@",
|
|
||||||
68: "$",
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each individual element of imgSet in ConvertToASCIISlice()
|
// For each individual element of imgSet in ConvertToASCIISlice()
|
||||||
const MAX_VAL float64 = 65535
|
const MAX_VAL float64 = 255
|
||||||
|
|
||||||
type AsciiChar struct {
|
type AsciiChar struct {
|
||||||
OriginalColor string
|
OriginalColor string
|
||||||
|
|
@ -119,24 +42,34 @@ type AsciiChar struct {
|
||||||
RgbValue [3]uint32
|
RgbValue [3]uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts the 2D image_conversions.AsciiPixel slice of image data (each instance representing each compressed pixel of original image)
|
/*
|
||||||
// to a 2D image_conversions.AsciiChar slice
|
Converts the 2D image_conversions.AsciiPixel slice of image data (each instance representing each compressed pixel of original image)
|
||||||
//
|
to a 2D image_conversions.AsciiChar slice
|
||||||
// If complex parameter is true, values are compared to 69 levels of color density in ASCII characters.
|
|
||||||
// Otherwise, values are compared to 10 levels of color density in ASCII characters.
|
If complex parameter is true, values are compared to 70 levels of color density in ASCII characters.
|
||||||
func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex bool, customMap string, fontColor [3]int) [][]AsciiChar {
|
Otherwise, values are compared to 10 levels of color density in ASCII characters.
|
||||||
|
*/
|
||||||
|
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])
|
||||||
|
|
||||||
var chosenTable map[int]string
|
chosenTable := map[int]string{}
|
||||||
|
|
||||||
|
// Turn ascii character-set string into map[int]string{} literal
|
||||||
if customMap == "" {
|
if customMap == "" {
|
||||||
|
var charSet string
|
||||||
|
|
||||||
if complex {
|
if complex {
|
||||||
chosenTable = asciiTableDetailed
|
charSet = asciiTableDetailed
|
||||||
} else {
|
} else {
|
||||||
chosenTable = asciiTableSimple
|
charSet = asciiTableSimple
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for index, char := range charSet {
|
||||||
|
chosenTable[index] = string(char)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
chosenTable = map[int]string{}
|
chosenTable = map[int]string{}
|
||||||
|
|
||||||
|
|
@ -145,10 +78,7 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([][]AsciiChar, height)
|
var result [][]AsciiChar
|
||||||
for i := range result {
|
|
||||||
result[i] = make([]AsciiChar, width)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < height; i++ {
|
for i := 0; i < height; i++ {
|
||||||
|
|
||||||
|
|
@ -192,25 +122,37 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex bool,
|
||||||
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.OriginalColor = color.Sprintf("<fg="+rStr+","+gStr+","+bStr+">%v</>", chosenTable[tempInt])
|
asciiChar := chosenTable[tempInt]
|
||||||
|
char.Simple = asciiChar
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if colorBg {
|
||||||
|
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), asciiChar, true)
|
||||||
|
} else {
|
||||||
|
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]
|
||||||
|
|
||||||
char.SetColor = color.Sprintf("<fg="+fcR+","+fcG+","+fcB+">%v</>", chosenTable[tempInt])
|
if colorBg {
|
||||||
|
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), asciiChar, true)
|
||||||
|
} else {
|
||||||
|
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), asciiChar, false)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char.Simple = chosenTable[tempInt]
|
|
||||||
|
|
||||||
if colored {
|
if colored {
|
||||||
char.RgbValue = imgSet[i][j].rgbValue
|
char.RgbValue = imgSet[i][j].rgbValue
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -219,8 +161,123 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex bool,
|
||||||
|
|
||||||
tempSlice = append(tempSlice, char)
|
tempSlice = append(tempSlice, char)
|
||||||
}
|
}
|
||||||
result[i] = tempSlice
|
result = append(result, tempSlice)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Converts the 2D image_conversions.AsciiPixel slice of image data (each instance representing each compressed pixel of original image)
|
||||||
|
to a 2D image_conversions.AsciiChar slice
|
||||||
|
|
||||||
|
Unlike ConvertToAsciiChars(), this function calculates braille characters instead of ascii
|
||||||
|
*/
|
||||||
|
func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, grayscale, colorBg bool, fontColor [3]int, threshold int) ([][]AsciiChar, error) {
|
||||||
|
|
||||||
|
BrailleThreshold = uint32(threshold)
|
||||||
|
|
||||||
|
height := len(imgSet)
|
||||||
|
width := len(imgSet[0])
|
||||||
|
|
||||||
|
var result [][]AsciiChar
|
||||||
|
|
||||||
|
for i := 0; i < height; i += 4 {
|
||||||
|
|
||||||
|
var tempSlice []AsciiChar
|
||||||
|
|
||||||
|
for j := 0; j < width; j += 2 {
|
||||||
|
|
||||||
|
brailleChar := getBrailleChar(i, j, negative, imgSet)
|
||||||
|
|
||||||
|
var r, g, b int
|
||||||
|
|
||||||
|
if colored {
|
||||||
|
r = int(imgSet[i][j].rgbValue[0])
|
||||||
|
g = int(imgSet[i][j].rgbValue[1])
|
||||||
|
b = int(imgSet[i][j].rgbValue[2])
|
||||||
|
} else {
|
||||||
|
r = int(imgSet[i][j].grayscaleValue[0])
|
||||||
|
g = int(imgSet[i][j].grayscaleValue[1])
|
||||||
|
b = int(imgSet[i][j].grayscaleValue[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
if negative {
|
||||||
|
// Select character from opposite side of table as well as turn pixels negative
|
||||||
|
r = 255 - r
|
||||||
|
g = 255 - g
|
||||||
|
b = 255 - b
|
||||||
|
|
||||||
|
if colored {
|
||||||
|
imgSet[i][j].rgbValue = [3]uint32{uint32(r), uint32(g), uint32(b)}
|
||||||
|
} else {
|
||||||
|
imgSet[i][j].grayscaleValue = [3]uint32{uint32(r), uint32(g), uint32(b)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var char AsciiChar
|
||||||
|
|
||||||
|
char.Simple = brailleChar
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if colorBg {
|
||||||
|
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), brailleChar, true)
|
||||||
|
} else {
|
||||||
|
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 fontColor != [3]int{255, 255, 255} {
|
||||||
|
fcR := fontColor[0]
|
||||||
|
fcG := fontColor[1]
|
||||||
|
fcB := fontColor[2]
|
||||||
|
|
||||||
|
if colorBg {
|
||||||
|
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), brailleChar, true)
|
||||||
|
} else {
|
||||||
|
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), brailleChar, false)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if colored {
|
||||||
|
char.RgbValue = imgSet[i][j].rgbValue
|
||||||
|
} else {
|
||||||
|
char.RgbValue = imgSet[i][j].grayscaleValue
|
||||||
|
}
|
||||||
|
|
||||||
|
tempSlice = append(tempSlice, char)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, tempSlice)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the BrailleStruct table to see which dots need to be highlighted
|
||||||
|
func getBrailleChar(x, y int, negative bool, imgSet [][]AsciiPixel) string {
|
||||||
|
|
||||||
|
brailleChar := 0x2800
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
for j := 0; j < 2; j++ {
|
||||||
|
if negative {
|
||||||
|
if imgSet[x+i][y+j].charDepth <= BrailleThreshold {
|
||||||
|
brailleChar += BrailleStruct[i][j]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if imgSet[x+i][y+j].charDepth >= BrailleThreshold {
|
||||||
|
brailleChar += BrailleStruct[i][j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(brailleChar)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,8 @@ limitations under the License.
|
||||||
package image_conversions
|
package image_conversions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"github.com/TheZoraiz/ascii-image-converter/aic_package/winsize"
|
|
||||||
"github.com/nfnt/resize"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AsciiPixel struct {
|
type AsciiPixel struct {
|
||||||
|
|
@ -31,121 +27,30 @@ type AsciiPixel struct {
|
||||||
rgbValue [3]uint32
|
rgbValue [3]uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function shrinks the passed image according to passed dimensions or terminal
|
/*
|
||||||
// size if none are passed. Stores each pixel's grayscale and RGB values in an AsciiPixel
|
This function shrinks the passed image according to specified or default dimensions.
|
||||||
// instance to simplify getting numeric data for ASCII character comparison.
|
Stores each pixel's grayscale and RGB values in an AsciiPixel instance to simplify
|
||||||
//
|
getting numeric data for ASCII character comparison.
|
||||||
// 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, width, height int, flipX, flipY, full bool) ([][]AsciiPixel, error) {
|
|
||||||
|
|
||||||
var asciiWidth, asciiHeight int
|
The returned 2D AsciiPixel slice contains each corresponding pixel's values
|
||||||
var smallImg image.Image
|
*/
|
||||||
|
func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full, isBraille, dither bool) ([][]AsciiPixel, error) {
|
||||||
|
|
||||||
|
smallImg, err := resizeImage(img, full, isBraille, dimensions, width, height)
|
||||||
|
|
||||||
terminalWidth, terminalHeight, err := winsize.GetTerminalSize()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if full {
|
// We mainatin a dithered image literal along with original image
|
||||||
asciiWidth = terminalWidth - 1
|
// The colors are kept from original image
|
||||||
|
var ditheredImage image.Image
|
||||||
|
|
||||||
// Passing 0 in place of width keeps the original image's aspect ratio
|
if isBraille && dither {
|
||||||
smallImg = resize.Resize(uint(asciiWidth), 0, img, resize.Lanczos3)
|
ditheredImage = ditherImage(smallImg)
|
||||||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
|
||||||
|
|
||||||
// To fix aspect ratio in eventual ascii art
|
|
||||||
asciiHeight = int(0.5 * float32(asciiHeight))
|
|
||||||
|
|
||||||
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
|
|
||||||
|
|
||||||
} else if (width != 0 || height != 0) && len(dimensions) == 0 {
|
|
||||||
// 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 is set and height is not set, use width to calculate aspect ratio
|
|
||||||
|
|
||||||
asciiWidth = width
|
|
||||||
|
|
||||||
smallImg = resize.Resize(uint(asciiWidth), 0, img, resize.Lanczos3)
|
|
||||||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
|
||||||
|
|
||||||
asciiHeight = int(0.5 * float32(asciiHeight))
|
|
||||||
if asciiHeight == 0 {
|
|
||||||
asciiHeight = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
|
|
||||||
|
|
||||||
} else if height != 0 && width == 0 {
|
|
||||||
// If height is set and width is not set, use height to calculate aspect ratio
|
|
||||||
|
|
||||||
asciiHeight = height
|
|
||||||
|
|
||||||
smallImg = resize.Resize(0, uint(asciiHeight), img, resize.Lanczos3)
|
|
||||||
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
|
|
||||||
|
|
||||||
asciiWidth = int(2 * float32(asciiWidth))
|
|
||||||
|
|
||||||
if asciiWidth > terminalWidth-1 {
|
|
||||||
return nil, fmt.Errorf("width calculated with aspect ratio exceeds terminal width")
|
|
||||||
}
|
|
||||||
|
|
||||||
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("both width and height can't be set. Use dimensions instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if len(dimensions) == 0 {
|
|
||||||
// This condition calculates aspect ratio according to terminal height
|
|
||||||
|
|
||||||
asciiHeight = terminalHeight - 1
|
|
||||||
|
|
||||||
smallImg = resize.Resize(0, uint(asciiHeight), img, resize.Lanczos3)
|
|
||||||
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
|
|
||||||
|
|
||||||
// To fix aspect ratio in eventual ascii art
|
|
||||||
asciiWidth = int(2 * float32(asciiWidth))
|
|
||||||
|
|
||||||
// If ascii width exceeds terminal width, change ratio with respect to terminal width
|
|
||||||
if asciiWidth >= terminalWidth {
|
|
||||||
asciiWidth = terminalWidth - 1
|
|
||||||
|
|
||||||
smallImg = resize.Resize(uint(asciiWidth), 0, img, resize.Lanczos3)
|
|
||||||
|
|
||||||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
|
||||||
|
|
||||||
// To fix aspect ratio in eventual ascii art
|
|
||||||
asciiHeight = int(0.5 * float32(asciiHeight))
|
|
||||||
}
|
|
||||||
|
|
||||||
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
asciiWidth = dimensions[0]
|
|
||||||
asciiHeight = dimensions[1]
|
|
||||||
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeated despite being in cmd/root.go to maintain support for library
|
var imgSet [][]AsciiPixel
|
||||||
//
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize imgSet 2D slice
|
|
||||||
imgSet := make([][]AsciiPixel, asciiHeight)
|
|
||||||
for i := range imgSet {
|
|
||||||
imgSet[i] = make([]AsciiPixel, asciiWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
b := smallImg.Bounds()
|
b := smallImg.Bounds()
|
||||||
|
|
||||||
|
|
@ -159,11 +64,24 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
||||||
grayPixel := color.GrayModel.Convert(oldPixel)
|
grayPixel := color.GrayModel.Convert(oldPixel)
|
||||||
|
|
||||||
r1, g1, b1, _ := grayPixel.RGBA()
|
r1, g1, b1, _ := grayPixel.RGBA()
|
||||||
charDepth := r1 // Only Red is needed from RGB for charDepth in AsciiPixel since they have the same value for grayscale images
|
charDepth := r1 / 257 // Only Red is needed from RGB for charDepth in AsciiPixel since they have the same value for grayscale images
|
||||||
r1 = uint32(r1 / 257)
|
r1 = uint32(r1 / 257)
|
||||||
g1 = uint32(g1 / 257)
|
g1 = uint32(g1 / 257)
|
||||||
b1 = uint32(b1 / 257)
|
b1 = uint32(b1 / 257)
|
||||||
|
|
||||||
|
if isBraille && dither {
|
||||||
|
|
||||||
|
// Change charDepth if image dithering is applied
|
||||||
|
// Note that neither grayscale nor original color values are changed.
|
||||||
|
// Only charDepth is kept from dithered image. This is because a
|
||||||
|
// dithered image loses its colors so it's only used to check braille
|
||||||
|
// dots' visibility
|
||||||
|
|
||||||
|
ditheredGrayPixel := color.GrayModel.Convert(ditheredImage.At(x, y))
|
||||||
|
charDepth, _, _, _ = ditheredGrayPixel.RGBA()
|
||||||
|
charDepth = charDepth / 257
|
||||||
|
}
|
||||||
|
|
||||||
// Get co1ored RGB values of original pixel for rgbValue in AsciiPixel
|
// Get co1ored RGB values of original pixel for rgbValue in AsciiPixel
|
||||||
r2, g2, b2, _ := oldPixel.RGBA()
|
r2, g2, b2, _ := oldPixel.RGBA()
|
||||||
r2 = uint32(r2 / 257)
|
r2 = uint32(r2 / 257)
|
||||||
|
|
@ -177,7 +95,7 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
imgSet[y] = temp
|
imgSet = append(imgSet, temp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This rarely affects performance since the ascii art 2D slice size isn't that large
|
// This rarely affects performance since the ascii art 2D slice size isn't that large
|
||||||
|
|
@ -187,22 +105,3 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
||||||
|
|
||||||
return imgSet, nil
|
return imgSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func reverse(imgSet [][]AsciiPixel, flipX, flipY bool) [][]AsciiPixel {
|
|
||||||
|
|
||||||
if flipX {
|
|
||||||
for _, row := range imgSet {
|
|
||||||
for i, j := 0, len(row)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
row[i], row[j] = row[j], row[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if flipY {
|
|
||||||
for i, j := 0, len(imgSet)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
imgSet[i], imgSet[j] = imgSet[j], imgSet[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return imgSet
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
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 image_conversions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/TheZoraiz/ascii-image-converter/aic_package/winsize"
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
|
gookitColor "github.com/gookit/color"
|
||||||
|
"github.com/makeworld-the-better-one/dither/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ditherImage(img image.Image) image.Image {
|
||||||
|
|
||||||
|
palette := []color.Color{
|
||||||
|
color.Black,
|
||||||
|
color.White,
|
||||||
|
}
|
||||||
|
|
||||||
|
d := dither.NewDitherer(palette)
|
||||||
|
d.Matrix = dither.FloydSteinberg
|
||||||
|
|
||||||
|
return d.DitherCopy(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeImage(img image.Image, full, 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 full {
|
||||||
|
terminalWidth, _, err := winsize.GetTerminalSize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
asciiWidth = terminalWidth - 1
|
||||||
|
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
||||||
|
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||||
|
|
||||||
|
} else if (width != 0 || height != 0) && len(dimensions) == 0 {
|
||||||
|
// If either width or height is set and dimensions aren't given
|
||||||
|
|
||||||
|
if width != 0 && height == 0 {
|
||||||
|
// If width is set and height is not set, use width to calculate aspect ratio
|
||||||
|
|
||||||
|
asciiWidth = width
|
||||||
|
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
||||||
|
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||||
|
|
||||||
|
if asciiHeight == 0 {
|
||||||
|
asciiHeight = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if height != 0 && width == 0 {
|
||||||
|
// If height is set and width is not set, use height to calculate aspect ratio
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// This condition calculates aspect ratio according to terminal height
|
||||||
|
|
||||||
|
terminalWidth, terminalHeight, err := winsize.GetTerminalSize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
asciiHeight = terminalHeight - 1
|
||||||
|
asciiWidth = int(float64(asciiHeight) * aspectRatio)
|
||||||
|
asciiWidth = int(2 * float64(asciiWidth))
|
||||||
|
|
||||||
|
// If ascii width exceeds terminal width, change ratio with respect to terminal width
|
||||||
|
if asciiWidth >= terminalWidth {
|
||||||
|
asciiWidth = terminalWidth - 1
|
||||||
|
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
||||||
|
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Else, set passed dimensions
|
||||||
|
|
||||||
|
asciiWidth = dimensions[0]
|
||||||
|
asciiHeight = dimensions[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because one braille character has 8 dots (4 rows and 2 columns)
|
||||||
|
if isBraille {
|
||||||
|
asciiWidth *= 2
|
||||||
|
asciiHeight *= 4
|
||||||
|
}
|
||||||
|
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
||||||
|
|
||||||
|
return smallImg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverse(imgSet [][]AsciiPixel, flipX, flipY bool) [][]AsciiPixel {
|
||||||
|
|
||||||
|
if flipX {
|
||||||
|
for _, row := range imgSet {
|
||||||
|
for i, j := 0, len(row)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
row[i], row[j] = row[j], row[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flipY {
|
||||||
|
for i, j := 0, len(imgSet)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
imgSet[i], imgSet[j] = imgSet[j], imgSet[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,10 +1,11 @@
|
||||||
name: ascii-image-converter
|
name: ascii-image-converter
|
||||||
base: core18
|
base: core18
|
||||||
version: "1.6.0"
|
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
|
||||||
them out onto the console. Supported input formats are JPEG/JPG, PNG, WEBP, BMP, TIFF/TIF and GIF
|
them out onto the console. Supported input formats are JPEG/JPG, PNG, WEBP, BMP, TIFF/TIF and GIF.
|
||||||
|
Now supports braille art.
|
||||||
|
|
||||||
grade: stable
|
grade: stable
|
||||||
confinement: strict
|
confinement: strict
|
||||||
|
|
|
||||||