Skip to content

Commit

Permalink
Added braille art support
Browse files Browse the repository at this point in the history
  • Loading branch information
TheZoraiz committed Sep 2, 2021
1 parent 1f3cd6a commit f318be9
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 71 deletions.
67 changes: 47 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,38 @@
![Github All Releases](https://img.shields.io/github/downloads/TheZoraiz/ascii-image-converter/total?color=brightgreen&label=Release%20Downloads)
[![ascii-image-converter](https://snapcraft.io/ascii-image-converter/badge.svg)](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:
* JPEG/JPG
* PNG
* BMP
* WEBP
* TIFF/TIF
* GIF (Experimental)
* GIF

<br>
Single image:

<p align="center">
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/base.gif">
</p>

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>



## Table of Contents

Expand Down Expand Up @@ -130,24 +153,6 @@ Example:
```
ascii-image-converter myImage.jpeg
```
<br>
Single image:

<p align="center">
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/base.gif">
</p>

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

Expand All @@ -165,6 +170,28 @@ ascii-image-converter [image paths/urls] --color
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/color.gif">
</p>

#### --braille OR -b

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 when converting each pixel into a dot. Value must be between 0 and 255.

Example:
```
ascii-image-converter [image paths/urls] -b --threshold 170
```

#### --dimensions OR -d

> **Note:** Don't immediately append another flag with -d
Expand Down
26 changes: 18 additions & 8 deletions aic_package/convert_gif.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ as an ascii art gif.
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 []byte, localGif *os.File) error {

var (
originalGif *gif.GIF
Expand All @@ -58,7 +58,12 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
originalGif, err = gif.DecodeAll(localGif)
}
if err != nil {
return "", fmt.Errorf("can't decode %v: %v", gifPath, err)
return fmt.Errorf("can't decode %v: %v", gifPath, err)
}

// Error handled earlier to save time in case of long gif processing
if braille && saveGifPath != "" {
return fmt.Errorf("saving braille art as a gif is not supported")
}

var (
Expand Down Expand Up @@ -97,13 +102,18 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l

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)
if err != nil {
fmt.Println("Error:", err)
os.Exit(0)
}

asciiCharSet := imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, customMap, fontColor)
var asciiCharSet [][]imgManip.AsciiChar
if braille {
asciiCharSet = imgManip.ConvertToBrailleChars(imgSet, negative, colored, fontColor, threshold)
} else {
asciiCharSet = imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, customMap, fontColor)
}
gifFramesSlice[i].asciiCharSet = asciiCharSet
gifFramesSlice[i].delay = originalGif.Delay[i]

Expand Down Expand Up @@ -137,12 +147,12 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l

saveFileName, err := createSaveFileName(gifPath, urlImgName, "-ascii-art.gif")
if err != nil {
return "", err
return err
}

fullPathName, err := getFullSavePath(saveFileName, saveGifPath)
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
Expand Down Expand Up @@ -219,7 +229,7 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l

gifFile, err := os.OpenFile(fullPathName, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return "", fmt.Errorf("can't save file: %v", err)
return fmt.Errorf("can't save file: %v", err)
}
defer gifFile.Close()

Expand Down Expand Up @@ -248,5 +258,5 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
}
}

return "", nil
return nil
}
13 changes: 11 additions & 2 deletions aic_package/convert_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,24 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
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)
if err != nil {
return "", err
}

asciiSet := imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, customMap, fontColor)
var asciiSet [][]imgManip.AsciiChar

if braille {
asciiSet = imgManip.ConvertToBrailleChars(imgSet, negative, colored, fontColor, threshold)
} else {
asciiSet = imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, customMap, fontColor)
}

// Save ascii art as .png image before printing it, if --save-img flag is passed
if saveImagePath != "" {
if braille {
return "", fmt.Errorf("saving braille art as an image is not supported")
}
if err := createImageToSave(
asciiSet,
colored || grayscale,
Expand Down
6 changes: 5 additions & 1 deletion aic_package/convert_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ func DefaultFlags() Flags {
FontFilePath: "",
FontColor: [3]int{255, 255, 255},
SaveBackgroundColor: [3]int{0, 0, 0},
Braille: false,
Threshold: 128,
}
}

Expand Down Expand Up @@ -88,6 +90,8 @@ func Convert(filePath string, flags Flags) (string, error) {
fontPath = flags.FontFilePath
fontColor = flags.FontColor
saveBgColor = flags.SaveBackgroundColor
braille = flags.Braille
threshold = flags.Threshold

// Declared at the start since some variables are initially used in conditional blocks
var (
Expand Down Expand Up @@ -141,7 +145,7 @@ func Convert(filePath string, flags Flags) (string, error) {
}

if path.Ext(filePath) == ".gif" {
return pathIsGif(filePath, urlImgName, pathIsURl, urlImgBytes, localFile)
return "", pathIsGif(filePath, urlImgName, pathIsURl, urlImgBytes, localFile)
} else {
return pathIsImage(filePath, urlImgName, pathIsURl, urlImgBytes, localFile)
}
Expand Down
14 changes: 7 additions & 7 deletions aic_package/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,24 @@ func flattenAscii(asciiSet [][]imgManip.AsciiChar, colored, toSaveTxt bool) []st
var ascii []string

for _, line := range asciiSet {
var tempAscii []string
var tempAscii string

for i := 0; i < len(line); i++ {
for _, char := range line {
if toSaveTxt {
tempAscii = append(tempAscii, line[i].Simple)
tempAscii += char.Simple
continue
}

if colored {
tempAscii = append(tempAscii, line[i].OriginalColor)
tempAscii += char.OriginalColor
} else if fontColor != [3]int{255, 255, 255} {
tempAscii = append(tempAscii, line[i].SetColor)
tempAscii += char.SetColor
} else {
tempAscii = append(tempAscii, line[i].Simple)
tempAscii += char.Simple
}
}

ascii = append(ascii, strings.Join(tempAscii, ""))
ascii = append(ascii, tempAscii)
}

return ascii
Expand Down
15 changes: 13 additions & 2 deletions aic_package/vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,22 @@ type Flags struct {
// This will be ignored if Flags.SaveImagePath or Flags.SaveGifPath are not set
FontFilePath string

// Font RGB color in saved png or gif files.
// This will be ignored if Flags.SaveImagePath or Flags.SaveGifPath are not set
// Font RGB color for terminal display and saved png or gif files.
FontColor [3]int

// Background RGB color in saved png or gif files.
// This will be ignored if Flags.SaveImagePath or Flags.SaveGifPath are not set
SaveBackgroundColor [3]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
}

var (
Expand All @@ -101,4 +110,6 @@ var (
fontPath string
fontColor [3]int
saveBgColor [3]int
braille bool
threshold int
)
14 changes: 11 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ var (
fontFile string
fontColor []int
saveBgColor []int
braille bool
threshold int

// Root commands
rootCmd = &cobra.Command{
Use: "ascii-image-converter [image paths/urls]",
Short: "Converts images and gifs into ascii art",
Version: "1.6.0",
Version: "1.7.0",
Long: "This tool converts images into ascii art and prints them on the terminal.\nFurther configuration can be managed with flags.",

// Not RunE since help text is getting larger and seeing it for every error impacts user experience
Expand Down Expand Up @@ -81,6 +83,8 @@ var (
FontFilePath: fontFile,
FontColor: [3]int{fontColor[0], fontColor[1], fontColor[2]},
SaveBackgroundColor: [3]int{saveBgColor[0], saveBgColor[1], saveBgColor[2]},
Braille: braille,
Threshold: threshold,
}

for _, imagePath := range args {
Expand Down Expand Up @@ -119,12 +123,14 @@ func init() {
rootCmd.Flags().SortFlags = false

// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ascii-image-converter.yaml)")
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with 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\n(Inverts with --negative flag)\n(Overrides --grayscale and --font-color 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(&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().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().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(&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")
Expand All @@ -141,6 +147,8 @@ func init() {
rootCmd.PersistentFlags().BoolP("help", "h", false, "Help for "+rootCmd.Name()+"\n")
rootCmd.PersistentFlags().BoolP("version", "v", false, "Version for "+rootCmd.Name())

rootCmd.SetVersionTemplate("{{printf \"v%s\" .Version}}\n")

defaultUsageTemplate := rootCmd.UsageTemplate()
rootCmd.SetUsageTemplate(defaultUsageTemplate + "\nCopyright © 2021 Zoraiz Hassan <[email protected]>\n" +
"Distributed under the Apache License Version 2.0 (Apache-2.0)\n" +
Expand Down
11 changes: 10 additions & 1 deletion cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func checkInputAndFlags(args []string) bool {
"WEBP\n" +
"BMP\n" +
"TIFF/TIF\n" +
"GIF (Experimental)\n\n")
"GIF\n\n")
return true
}

Expand Down Expand Up @@ -171,5 +171,14 @@ 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
}

return false
}
Loading

0 comments on commit f318be9

Please sign in to comment.