Added braille art support
This commit is contained in:
parent
1f3cd6ad00
commit
f318be9c56
67
README.md
67
README.md
|
|
@ -6,7 +6,9 @@
|
|||

|
||||
[](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
|
||||
|
|
@ -14,7 +16,28 @@ Input formats currently supported:
|
|||
* 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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 (
|
||||
|
|
@ -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]
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -248,5 +258,5 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
|
|||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ func DefaultFlags() Flags {
|
|||
FontFilePath: "",
|
||||
FontColor: [3]int{255, 255, 255},
|
||||
SaveBackgroundColor: [3]int{0, 0, 0},
|
||||
Braille: false,
|
||||
Threshold: 128,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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 (
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
@ -101,4 +110,6 @@ var (
|
|||
fontPath string
|
||||
fontColor [3]int
|
||||
saveBgColor [3]int
|
||||
braille bool
|
||||
threshold int
|
||||
)
|
||||
|
|
|
|||
14
cmd/root.go
14
cmd/root.go
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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")
|
||||
|
|
@ -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 <hzoraiz8@gmail.com>\n" +
|
||||
"Distributed under the Apache License Version 2.0 (Apache-2.0)\n" +
|
||||
|
|
|
|||
11
cmd/util.go
11
cmd/util.go
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,12 +22,24 @@ import (
|
|||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
||||
var asciiTableSimple = " .:-=+*#%@"
|
||||
var asciiTableDetailed = " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"
|
||||
var (
|
||||
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
||||
asciiTableSimple = " .:-=+*#%@"
|
||||
asciiTableDetailed = " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"
|
||||
|
||||
// Structure for braille dots
|
||||
BrailleStruct = [4][2]int{
|
||||
{0x1, 0x8},
|
||||
{0x2, 0x10},
|
||||
{0x4, 0x20},
|
||||
{0x40, 0x80},
|
||||
}
|
||||
|
||||
BrailleThreshold uint32
|
||||
)
|
||||
|
||||
// For each individual element of imgSet in ConvertToASCIISlice()
|
||||
const MAX_VAL float64 = 65535
|
||||
const MAX_VAL float64 = 255
|
||||
|
||||
type AsciiChar struct {
|
||||
OriginalColor string
|
||||
|
|
@ -72,10 +84,7 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex bool,
|
|||
}
|
||||
}
|
||||
|
||||
result := make([][]AsciiChar, height)
|
||||
for i := range result {
|
||||
result[i] = make([]AsciiChar, width)
|
||||
}
|
||||
var result [][]AsciiChar
|
||||
|
||||
for i := 0; i < height; i++ {
|
||||
|
||||
|
|
@ -146,8 +155,111 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex bool,
|
|||
|
||||
tempSlice = append(tempSlice, char)
|
||||
}
|
||||
result[i] = tempSlice
|
||||
result = append(result, tempSlice)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
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 bool, fontColor [3]int, threshold int) [][]AsciiChar {
|
||||
|
||||
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)}
|
||||
}
|
||||
}
|
||||
|
||||
rStr := strconv.Itoa(r)
|
||||
gStr := strconv.Itoa(g)
|
||||
bStr := strconv.Itoa(b)
|
||||
|
||||
var char AsciiChar
|
||||
|
||||
char.Simple = brailleChar
|
||||
char.OriginalColor = color.Sprintf("<fg="+rStr+","+gStr+","+bStr+">%v</>", brailleChar)
|
||||
|
||||
// If font color is not set, use a simple string. Otherwise, use True color
|
||||
if fontColor != [3]int{255, 255, 255} {
|
||||
fcR := strconv.Itoa(fontColor[0])
|
||||
fcG := strconv.Itoa(fontColor[1])
|
||||
fcB := strconv.Itoa(fontColor[2])
|
||||
|
||||
char.SetColor = color.Sprintf("<fg="+fcR+","+fcG+","+fcB+">%v</>", brailleChar)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ type AsciiPixel struct {
|
|||
rgbValue [3]uint32
|
||||
}
|
||||
|
||||
func resizeForBraille(asciiWidth, asciiHeight int) (int, int) {
|
||||
return asciiWidth * 2, asciiHeight * 4
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
|
|
@ -39,7 +43,7 @@ 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) {
|
||||
func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full, isBraille bool) ([][]AsciiPixel, error) {
|
||||
|
||||
var asciiWidth, asciiHeight int
|
||||
var smallImg image.Image
|
||||
|
|
@ -57,8 +61,11 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
|||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
||||
|
||||
// To fix aspect ratio in eventual ascii art
|
||||
asciiHeight = int(0.5 * float32(asciiHeight))
|
||||
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||
|
||||
if isBraille {
|
||||
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
||||
}
|
||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
||||
|
||||
} else if (width != 0 || height != 0) && len(dimensions) == 0 {
|
||||
|
|
@ -76,13 +83,11 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
|||
smallImg = imaging.Resize(img, asciiWidth, 0, imaging.Lanczos)
|
||||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
||||
|
||||
asciiHeight = int(0.5 * float32(asciiHeight))
|
||||
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||
if asciiHeight == 0 {
|
||||
asciiHeight = 1
|
||||
}
|
||||
|
||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
||||
|
||||
} else if height != 0 && width == 0 {
|
||||
// If height is set and width is not set, use height to calculate aspect ratio
|
||||
|
||||
|
|
@ -91,18 +96,21 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
|||
smallImg = imaging.Resize(img, 0, asciiHeight, imaging.Lanczos)
|
||||
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
|
||||
|
||||
asciiWidth = int(2 * float32(asciiWidth))
|
||||
asciiWidth = int(2 * float64(asciiWidth))
|
||||
|
||||
if asciiWidth > terminalWidth-1 {
|
||||
return nil, fmt.Errorf("width calculated with aspect ratio exceeds terminal width")
|
||||
}
|
||||
|
||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
||||
|
||||
} else {
|
||||
return nil, fmt.Errorf("both width and height can't be set. Use dimensions instead")
|
||||
}
|
||||
|
||||
if isBraille {
|
||||
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
||||
}
|
||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
||||
|
||||
} else if len(dimensions) == 0 {
|
||||
// This condition calculates aspect ratio according to terminal height
|
||||
|
||||
|
|
@ -112,7 +120,7 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
|||
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
|
||||
|
||||
// To fix aspect ratio in eventual ascii art
|
||||
asciiWidth = int(2 * float32(asciiWidth))
|
||||
asciiWidth = int(2 * float64(asciiWidth))
|
||||
|
||||
// If ascii width exceeds terminal width, change ratio with respect to terminal width
|
||||
if asciiWidth >= terminalWidth {
|
||||
|
|
@ -123,14 +131,21 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
|||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
||||
|
||||
// To fix aspect ratio in eventual ascii art
|
||||
asciiHeight = int(0.5 * float32(asciiHeight))
|
||||
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||
}
|
||||
|
||||
if isBraille {
|
||||
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
||||
}
|
||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
||||
|
||||
} else {
|
||||
asciiWidth = dimensions[0]
|
||||
asciiHeight = dimensions[1]
|
||||
|
||||
if isBraille {
|
||||
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
||||
}
|
||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
||||
}
|
||||
|
||||
|
|
@ -143,11 +158,7 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
|||
}
|
||||
}
|
||||
|
||||
// Initialize imgSet 2D slice
|
||||
imgSet := make([][]AsciiPixel, asciiHeight)
|
||||
for i := range imgSet {
|
||||
imgSet[i] = make([]AsciiPixel, asciiWidth)
|
||||
}
|
||||
var imgSet [][]AsciiPixel
|
||||
|
||||
b := smallImg.Bounds()
|
||||
|
||||
|
|
@ -161,7 +172,7 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
|||
grayPixel := color.GrayModel.Convert(oldPixel)
|
||||
|
||||
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)
|
||||
g1 = uint32(g1 / 257)
|
||||
b1 = uint32(b1 / 257)
|
||||
|
|
@ -179,7 +190,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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name: ascii-image-converter
|
||||
base: core18
|
||||
version: "1.6.0"
|
||||
version: "1.7.0"
|
||||
summary: Convert images and gifs into ascii art
|
||||
description: |
|
||||
ascii-image-converter is a command-line tool that converts images into ascii art and prints
|
||||
|
|
|
|||
Loading…
Reference in New Issue