Skip to content

Commit

Permalink
Added 8-bit color support (#9, #11)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheZoraiz committed Sep 10, 2021
1 parent dd8f652 commit df5eafe
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 45 deletions.
1 change: 0 additions & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ builds:
# - mipsle
# - mips64
# - mips64le


archives:
-
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,9 @@ ascii-image-converter myImage.jpeg

#### --color OR -C

> **Note:** Your terminal must support 24-bit colors for appropriate results
> **Note:** Your terminal must support 24-bit or 8-bit colors for appropriate results. If 24-bit colors aren't supported, 8-bit color escape codes will be used
Display ascii art with the colors from original image. Works with the --negative flag as well.
Display ascii art with the colors from original image.

```
ascii-image-converter [image paths/urls] -C
Expand Down
15 changes: 10 additions & 5 deletions aic_package/convert_gif.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,29 @@ 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
// positioned inside the original gif. This behavior isn't supported by this app
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")
fmt.Printf("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\n")
os.Exit(0)
}

var imgSet [][]imgManip.AsciiPixel

imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, width, height, flipX, flipY, full, braille, dither, noTermSizeComparison)
if err != nil {
fmt.Println("Error:", err)
fmt.Printf("Error: %v\n", err)
os.Exit(0)
}

var asciiCharSet [][]imgManip.AsciiChar
if braille {
asciiCharSet = imgManip.ConvertToBrailleChars(imgSet, negative, colored, colorBg, fontColor, threshold)
asciiCharSet, err = imgManip.ConvertToBrailleChars(imgSet, negative, colored, grayscale, colorBg, fontColor, threshold)
} else {
asciiCharSet = imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, colorBg, customMap, fontColor)
asciiCharSet, err = imgManip.ConvertToAsciiChars(imgSet, negative, colored, grayscale, complex, colorBg, customMap, fontColor)
}
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(0)
}

gifFramesSlice[i].asciiCharSet = asciiCharSet
gifFramesSlice[i].delay = originalGif.Delay[i]

Expand Down Expand Up @@ -187,7 +192,7 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
colored || grayscale,
)
if err != nil {
fmt.Println("Error:", err)
fmt.Printf("Error: %v\n", err)
os.Exit(0)
}

Expand Down
7 changes: 5 additions & 2 deletions aic_package/convert_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
var asciiSet [][]imgManip.AsciiChar

if braille {
asciiSet = imgManip.ConvertToBrailleChars(imgSet, negative, colored, colorBg, fontColor, threshold)
asciiSet, err = imgManip.ConvertToBrailleChars(imgSet, negative, colored, grayscale, colorBg, fontColor, threshold)
} else {
asciiSet = imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, colorBg, customMap, fontColor)
asciiSet, err = imgManip.ConvertToAsciiChars(imgSet, negative, colored, grayscale, complex, colorBg, customMap, fontColor)
}
if err != nil {
return "", err
}

// Save ascii art as .png image before printing it, if --save-img flag is passed
Expand Down
4 changes: 2 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ var (
rootCmd = &cobra.Command{
Use: "ascii-image-converter [image paths/urls]",
Short: "Converts images and gifs into ascii art",
Version: "1.9.2",
Version: "1.10.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 @@ -128,7 +128,7 @@ 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\nTerminal must support 24-bit colors\n(Inverts with --negative flag)\n(Overrides --grayscale and --font-color flags)\n")
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with original colors\nIf 24-bit colors aren't supported, uses 8-bit\n(Inverts with --negative flag)\n(Overrides --grayscale and --font-color flags)\n")
rootCmd.PersistentFlags().BoolVar(&colorBg, "color-bg", false, "If some color flag is passed, use that color\non character background instead of foreground\n(Inverts with --negative flag)\n(Only applicable for terminal display)\n")
rootCmd.PersistentFlags().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")
Expand Down
66 changes: 34 additions & 32 deletions image_manipulation/ascii_conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ limitations under the License.

package image_conversions

import (
"strconv"

"github.com/gookit/color"
)

var (
// Reference taken from http://paulbourke.net/dataformats/asciiart/
asciiTableSimple = " .:-=+*#%@"
Expand Down Expand Up @@ -55,7 +49,7 @@ to a 2D image_conversions.AsciiChar slice
If complex parameter is true, values are compared to 70 levels of color density in ASCII characters.
Otherwise, values are compared to 10 levels of color density in ASCII characters.
*/
func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex, colorBg bool, customMap string, fontColor [3]int) [][]AsciiChar {
func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, grayscale, complex, colorBg bool, customMap string, fontColor [3]int) ([][]AsciiChar, error) {

height := len(imgSet)
width := len(imgSet[0])
Expand Down Expand Up @@ -128,29 +122,33 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex, colo
tempInt = (len(chosenTable) - 1) - tempInt
}

rStr := strconv.Itoa(r)
gStr := strconv.Itoa(g)
bStr := strconv.Itoa(b)

var char AsciiChar

char.Simple = chosenTable[tempInt]

var err error
if colorBg {
char.OriginalColor = color.Sprintf("<bg="+rStr+","+gStr+","+bStr+">%v</>", chosenTable[tempInt])
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), chosenTable[tempInt], true)
} else {
char.OriginalColor = color.Sprintf("<fg="+rStr+","+gStr+","+bStr+">%v</>", chosenTable[tempInt])
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), chosenTable[tempInt], 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 := strconv.Itoa(fontColor[0])
fcG := strconv.Itoa(fontColor[1])
fcB := strconv.Itoa(fontColor[2])
fcR := fontColor[0]
fcG := fontColor[1]
fcB := fontColor[2]

if colorBg {
char.SetColor = color.Sprintf("<bg="+fcR+","+fcG+","+fcB+">%v</>", chosenTable[tempInt])
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), chosenTable[tempInt], true)
} else {
char.SetColor = color.Sprintf("<fg="+fcR+","+fcG+","+fcB+">%v</>", chosenTable[tempInt])
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), chosenTable[tempInt], false)
}
if err != nil {
return nil, err
}
}

Expand All @@ -165,7 +163,7 @@ func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex, colo
result = append(result, tempSlice)
}

return result
return result, nil
}

/*
Expand All @@ -174,7 +172,7 @@ to a 2D image_conversions.AsciiChar slice
Unlike ConvertToAsciiChars(), this function calculates braille characters instead of ascii
*/
func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, colorBg bool, fontColor [3]int, threshold int) [][]AsciiChar {
func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, grayscale, colorBg bool, fontColor [3]int, threshold int) ([][]AsciiChar, error) {

BrailleThreshold = uint32(threshold)

Expand Down Expand Up @@ -216,29 +214,33 @@ func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, colorBg boo
}
}

rStr := strconv.Itoa(r)
gStr := strconv.Itoa(g)
bStr := strconv.Itoa(b)

var char AsciiChar

char.Simple = brailleChar

var err error
if colorBg {
char.OriginalColor = color.Sprintf("<bg="+rStr+","+gStr+","+bStr+">%v</>", brailleChar)
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), brailleChar, true)
} else {
char.OriginalColor = color.Sprintf("<fg="+rStr+","+gStr+","+bStr+">%v</>", brailleChar)
char.OriginalColor, err = getColoredCharForTerm(uint8(r), uint8(g), uint8(b), brailleChar, false)
}
if (colored || grayscale) && err != nil {
return nil, err
}

// If font color is not set, use a simple string. Otherwise, use True color
if fontColor != [3]int{255, 255, 255} {
fcR := strconv.Itoa(fontColor[0])
fcG := strconv.Itoa(fontColor[1])
fcB := strconv.Itoa(fontColor[2])
fcR := fontColor[0]
fcG := fontColor[1]
fcB := fontColor[2]

if colorBg {
char.SetColor = color.Sprintf("<bg="+fcR+","+fcG+","+fcB+">%v</>", brailleChar)
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), brailleChar, true)
} else {
char.SetColor = color.Sprintf("<fg="+fcR+","+fcG+","+fcB+">%v</>", brailleChar)
char.SetColor, err = getColoredCharForTerm(uint8(fcR), uint8(fcG), uint8(fcB), brailleChar, false)
}
if err != nil {
return nil, err
}
}

Expand All @@ -254,7 +256,7 @@ func ConvertToBrailleChars(imgSet [][]AsciiPixel, negative, colored, colorBg boo
result = append(result, tempSlice)
}

return result
return result, nil
}

// Iterate through the BrailleStruct table to see which dots need to be highlighted
Expand Down
23 changes: 23 additions & 0 deletions image_manipulation/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"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"
)

Expand Down Expand Up @@ -200,3 +201,25 @@ func reverse(imgSet [][]AsciiPixel, flipX, flipY bool) [][]AsciiPixel {

return imgSet
}

var termColorLevel string = gookitColor.TermColorLevel().String()

// This functions calculates terminal color level between rgb colors, 256-colors, 16-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
}
2 changes: 1 addition & 1 deletion snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: ascii-image-converter
base: core18
version: "1.9.2"
version: "1.10.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
Expand Down

0 comments on commit df5eafe

Please sign in to comment.