Added dithering and improved performance

This commit is contained in:
Zoraiz 2021-09-06 17:34:49 +05:00
parent 8be1d4c9b5
commit 2db0093e58
13 changed files with 220 additions and 152 deletions

View File

@ -1,10 +1,10 @@
# ascii-image-converter # ascii-image-converter
[![release version](https://img.shields.io/github/v/release/TheZoraiz/ascii-image-converter?label=Latest%20Version)](https://github.com/TheZoraiz/ascii-image-converter/releases/latest) [![release-version](https://img.shields.io/github/v/release/TheZoraiz/ascii-image-converter?label=Latest%20Version)](https://github.com/TheZoraiz/ascii-image-converter/releases/latest)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt) [![license](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
[![ascii-image-converter-lang](https://img.shields.io/badge/Language-Go-blue)](https://golang.org/) [![language](https://img.shields.io/badge/Language-Go-blue)](https://golang.org/)
![Github All Releases](https://img.shields.io/github/downloads/TheZoraiz/ascii-image-converter/total?color=brightgreen&label=Release%20Downloads) ![release-downloads](https://img.shields.io/github/downloads/TheZoraiz/ascii-image-converter/total?color=1d872d&label=Release%20Downloads)
[![ascii-image-converter](https://snapcraft.io/ascii-image-converter/badge.svg)](https://snapcraft.io/ascii-image-converter) [![ascii-image-converter-snap](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. 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.
@ -135,8 +135,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.
@ -199,6 +198,19 @@ Example:
ascii-image-converter [image paths/urls] -b --threshold 170 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 #### --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` 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`
@ -492,6 +504,8 @@ You can fork the project and implement any changes you want for a pull request.
[github.com/asaskevich/govalidator](https://github.com/asaskevich/govalidator) [github.com/asaskevich/govalidator](https://github.com/asaskevich/govalidator)
[github.com/makeworld-the-better-one/dither/v2](https://github.com/makeworld-the-better-one/dither/v2)
## License ## License
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt) [Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)

View File

@ -97,7 +97,7 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
var imgSet [][]imgManip.AsciiPixel var imgSet [][]imgManip.AsciiPixel
imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, width, height, flipX, flipY, full, braille) imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, width, height, flipX, flipY, full, braille, dither)
if err != nil { if err != nil {
fmt.Println("Error:", err) fmt.Println("Error:", err)
os.Exit(0) os.Exit(0)

View File

@ -43,7 +43,7 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
return "", fmt.Errorf("can't decode %v: %v", imagePath, err) return "", fmt.Errorf("can't decode %v: %v", imagePath, err)
} }
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, width, height, flipX, flipY, full, braille) imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, width, height, flipX, flipY, full, braille, dither)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -60,6 +60,7 @@ func DefaultFlags() Flags {
SaveBackgroundColor: [3]int{0, 0, 0}, SaveBackgroundColor: [3]int{0, 0, 0},
Braille: false, Braille: false,
Threshold: 128, Threshold: 128,
Dither: false,
} }
} }
@ -94,6 +95,7 @@ func Convert(filePath string, flags Flags) (string, error) {
saveBgColor = flags.SaveBackgroundColor saveBgColor = flags.SaveBackgroundColor
braille = flags.Braille braille = flags.Braille
threshold = flags.Threshold threshold = flags.Threshold
dither = flags.Dither
// 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 (

View File

@ -94,6 +94,10 @@ type Flags struct {
// be between 0 and 255. Ideal value is 128. // be between 0 and 255. Ideal value is 128.
// This will be ignored if Flags.Braille is not set // This will be ignored if Flags.Braille is not set
Threshold int 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
} }
var ( var (
@ -117,4 +121,5 @@ var (
saveBgColor [3]int saveBgColor [3]int
braille bool braille bool
threshold int threshold int
dither bool
) )

View File

@ -51,12 +51,13 @@ var (
saveBgColor []int saveBgColor []int
braille bool braille bool
threshold int threshold int
dither 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]",
Short: "Converts images and gifs into ascii art", Short: "Converts images and gifs into ascii art",
Version: "1.8.0", Version: "1.9.0",
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
@ -87,6 +88,7 @@ var (
SaveBackgroundColor: [3]int{saveBgColor[0], saveBgColor[1], saveBgColor[2]}, SaveBackgroundColor: [3]int{saveBgColor[0], saveBgColor[1], saveBgColor[2]},
Braille: braille, Braille: braille,
Threshold: threshold, Threshold: threshold,
Dither: dither,
} }
for _, imagePath := range args { for _, imagePath := range args {
@ -126,13 +128,14 @@ func init() {
// 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(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\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(Doesn't work for --save-img or --save-gif)\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(&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().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().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(&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")

View File

@ -180,5 +180,10 @@ func checkInputAndFlags(args []string) bool {
return true return true
} }
if dither && !braille {
fmt.Printf("Error: image dithering is only reserved for --braille flag\n\n")
return true
}
return false return false
} }

BIN
example_gifs/dither.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

1
go.mod
View File

@ -10,6 +10,7 @@ require (
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/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/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

2
go.sum
View File

@ -124,6 +124,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=

View File

@ -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/disintegration/imaging"
) )
type AsciiPixel struct { type AsciiPixel struct {
@ -31,131 +27,26 @@ type AsciiPixel struct {
rgbValue [3]uint32 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 This function shrinks the passed image according to specified or default dimensions.
size if none are passed. Stores each pixel's grayscale and RGB values in an AsciiPixel Stores each pixel's grayscale and RGB values in an AsciiPixel instance to simplify
instance to simplify getting numeric data for ASCII character comparison. getting numeric data for ASCII character comparison.
The returned 2D AsciiPixel slice contains each corresponding pixel's values. Grayscale value The returned 2D AsciiPixel slice contains each corresponding pixel's values
ranges from 0 to 65535, while RGB values are separate.
*/ */
func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full, isBraille bool) ([][]AsciiPixel, error) { func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full, isBraille, dither bool) ([][]AsciiPixel, error) {
var asciiWidth, asciiHeight int smallImg, err := resizeImage(img, full, isBraille, dimensions, width, height)
var smallImg image.Image
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 = imaging.Resize(img, asciiWidth, 0, imaging.Lanczos) ditheredImage = ditherImage(smallImg)
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
// To fix aspect ratio in eventual ascii art
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 {
// 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 = imaging.Resize(img, asciiWidth, 0, imaging.Lanczos)
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
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
smallImg = imaging.Resize(img, 0, asciiHeight, imaging.Lanczos)
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
asciiWidth = int(2 * float64(asciiWidth))
if asciiWidth > terminalWidth-1 {
return nil, fmt.Errorf("width calculated with aspect ratio exceeds terminal width")
}
} 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
asciiHeight = terminalHeight - 1
smallImg = imaging.Resize(img, 0, asciiHeight, imaging.Lanczos)
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
// To fix aspect ratio in eventual ascii art
asciiWidth = int(2 * float64(asciiWidth))
// If ascii width exceeds terminal width, change ratio with respect to terminal width
if asciiWidth >= terminalWidth {
asciiWidth = terminalWidth - 1
smallImg = imaging.Resize(img, asciiWidth, 0, imaging.Lanczos)
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
// To fix aspect ratio in eventual ascii art
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)
}
// Repeated despite being in cmd/root.go to maintain support for library
//
// 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")
}
} }
var imgSet [][]AsciiPixel var imgSet [][]AsciiPixel
@ -177,6 +68,19 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
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)
@ -200,22 +104,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
}

151
image_manipulation/util.go Normal file
View File

@ -0,0 +1,151 @@
/*
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"
"github.com/makeworld-the-better-one/dither/v2"
)
func resizeForBraille(asciiWidth, asciiHeight int) (int, int) {
return asciiWidth * 2, asciiHeight * 4
}
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
terminalWidth, terminalHeight, err := winsize.GetTerminalSize()
if err != nil {
return nil, err
}
imgWidth := float64(img.Bounds().Dx())
imgHeight := float64(img.Bounds().Dy())
aspectRatio := imgWidth / imgHeight
if full {
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 > 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
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 > terminalWidth-1 {
return nil, fmt.Errorf("width calculated with aspect ratio exceeds terminal width")
}
} 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
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 {
asciiWidth = dimensions[0]
asciiHeight = dimensions[1]
}
// Repeated despite being in cmd/root.go to maintain support for library
//
// If there are passed dimensions, check whether the width exceeds terminal width
if len(dimensions) > 0 && !full {
if dimensions[0] > terminalWidth-1 {
return nil, fmt.Errorf("set width must be lower than terminal width")
}
}
if isBraille {
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
}
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
}

View File

@ -1,6 +1,6 @@
name: ascii-image-converter name: ascii-image-converter
base: core18 base: core18
version: "1.8.0" version: "1.9.0"
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