Added APT repo
This commit is contained in:
parent
a421952bfe
commit
ed3dfa1ed5
|
|
@ -13,10 +13,24 @@ builds:
|
|||
- windows
|
||||
|
||||
goarch:
|
||||
# Architectures for executable binary generation
|
||||
- amd64
|
||||
- arm64
|
||||
- arm
|
||||
- 386
|
||||
|
||||
# Specifying all architectures for .deb and .rpm generation
|
||||
# - 386
|
||||
# - amd64
|
||||
# - arm
|
||||
# - arm64
|
||||
# - ppc64
|
||||
# - ppc64le
|
||||
# - mips
|
||||
# - mipsle
|
||||
# - mips64
|
||||
# - mips64le
|
||||
|
||||
|
||||
archives:
|
||||
-
|
||||
|
|
@ -50,3 +64,25 @@ changelog:
|
|||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
|
||||
nfpms:
|
||||
-
|
||||
package_name: ascii-image-converter
|
||||
file_name_template: "{{ .ProjectName }}_{{ .Arch }}"
|
||||
|
||||
homepage: https://github.com/TheZoraiz/ascii-image-converter
|
||||
vendor: Zoraiz Hassan
|
||||
maintainer: Zoraiz Hassan <hzoraiz8@gmail.com>
|
||||
description: Convert images into ascii art
|
||||
license: Apache 2.0
|
||||
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
|
||||
release: 1
|
||||
section: graphics
|
||||
priority: optional
|
||||
|
||||
rpm:
|
||||
compression: lzma
|
||||
|
|
|
|||
38
README.md
38
README.md
|
|
@ -1,3 +1,5 @@
|
|||
|
||||
|
||||
# ascii-image-converter
|
||||
|
||||
[](https://github.com/TheZoraiz/ascii-image-converter/releases/latest)
|
||||
|
|
@ -21,6 +23,7 @@ Input formats currently supported:
|
|||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
* [Ubuntu / Ubuntu-based](#ubuntu-or-ubuntu-based-distros)
|
||||
* [Snap](#snap)
|
||||
* [Go](#go)
|
||||
* [Linux (binaries)](#linux)
|
||||
|
|
@ -34,11 +37,25 @@ Input formats currently supported:
|
|||
|
||||
## Installation
|
||||
|
||||
### Ubuntu or Ubuntu-based Distros
|
||||
|
||||
Execute the following commands in order:
|
||||
|
||||
```
|
||||
echo 'deb [trusted=yes] https://apt.fury.io/zoraiz/ /' | sudo tee -a /etc/apt/sources.list.d/fury.list
|
||||
```
|
||||
```
|
||||
sudo apt update
|
||||
```
|
||||
```
|
||||
sudo apt install -y ascii-image-converter
|
||||
```
|
||||
|
||||
### Snap
|
||||
|
||||
You can download through snap.
|
||||
|
||||
Note: The snap will not have access to hidden files and files outside the $HOME directory. This includes write access for the ascii art saving files as well.
|
||||
Note: The snap will not have access to hidden images and images outside the $HOME directory. This includes write access for saving ascii images and text files as well.
|
||||
|
||||
```
|
||||
sudo snap install ascii-image-converter
|
||||
|
|
@ -68,20 +85,20 @@ Now, open a terminal in the same directory and execute this command:
|
|||
```
|
||||
sudo cp ascii-image-converter /usr/local/bin/
|
||||
```
|
||||
Now you can use ascii-image-converter in the terminal. Execute `ascii-image-converter -h` for more details.
|
||||
Now you can use ascii-image-converter in the terminal. Execute "ascii-image-converter -h" for more details.
|
||||
|
||||
### Windows
|
||||
|
||||
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:
|
||||
* In Search, search for and then select "view advanced system settings" (Control Panel)
|
||||
* 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` on the right side and then paste the path of the folder that you copied initially.
|
||||
* Click `Ok` on all open windows.
|
||||
* In Search, search for and then select: System (Control Panel)
|
||||
* Click the Advanced System settings link.
|
||||
* 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.
|
||||
* Click "Ok" on all open windows.
|
||||
|
||||
Now, restart any open command prompt and execute `ascii-image-converter -h` for more details.
|
||||
Now, restart any open command prompt and execute "ascii-image-converter -h" for more details.
|
||||
|
||||
<br>
|
||||
|
||||
|
|
@ -256,11 +273,6 @@ Note: This is an experimental feature and may not result in the finest quality G
|
|||
|
||||
Saves the passed GIF as an ascii art GIF with the name `<image-name>-ascii-art.gif` in the directory path passed to the flag.
|
||||
|
||||
Example for current directory:
|
||||
```
|
||||
ascii-image-converter [gif path/url] --save-gif .
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/save.gif">
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
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 aic_package
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color/palette"
|
||||
"image/draw"
|
||||
"image/gif"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
imgManip "github.com/TheZoraiz/ascii-image-converter/image_manipulation"
|
||||
)
|
||||
|
||||
type GifFrame struct {
|
||||
asciiCharSet [][]imgManip.AsciiChar
|
||||
delay int
|
||||
}
|
||||
|
||||
/*
|
||||
This function grabs each image frame from passed gif and turns it into ascii art. If SaveGifPath flag is passed,
|
||||
it'll turn each ascii art into an image instance of the same dimensions as the original gif and save them
|
||||
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) {
|
||||
|
||||
var (
|
||||
originalGif *gif.GIF
|
||||
err error
|
||||
)
|
||||
|
||||
if pathIsURl {
|
||||
originalGif, err = gif.DecodeAll(bytes.NewReader(urlImgBytes))
|
||||
} else {
|
||||
originalGif, err = gif.DecodeAll(localGif)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't decode %v: %v", gifPath, err)
|
||||
}
|
||||
|
||||
var (
|
||||
asciiArtSet = make([]string, len(originalGif.Image))
|
||||
gifFramesSlice = make([]GifFrame, len(originalGif.Image))
|
||||
|
||||
counter = 0
|
||||
concurrentProcesses = 0
|
||||
wg sync.WaitGroup
|
||||
hostCpuCount = runtime.NumCPU()
|
||||
)
|
||||
|
||||
fmt.Printf("Generating ascii art... 0%%\r")
|
||||
|
||||
// Get first frame of gif and its dimensions
|
||||
firstGifFrame := originalGif.Image[0].SubImage(originalGif.Image[0].Rect)
|
||||
firstGifFrameWidth := firstGifFrame.Bounds().Dx()
|
||||
firstGifFrameHeight := firstGifFrame.Bounds().Dy()
|
||||
|
||||
// Multi-threaded loop to decrease execution time
|
||||
for i, frame := range originalGif.Image {
|
||||
|
||||
wg.Add(1)
|
||||
concurrentProcesses++
|
||||
|
||||
go func(i int, frame *image.Paletted) {
|
||||
|
||||
frameImage := frame.SubImage(frame.Rect)
|
||||
|
||||
// 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")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
var imgSet [][]imgManip.AsciiPixel
|
||||
|
||||
imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, flipX, flipY, full)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
asciiCharSet := imgManip.ConvertToAscii(imgSet, negative, colored, complex, customMap)
|
||||
gifFramesSlice[i].asciiCharSet = asciiCharSet
|
||||
gifFramesSlice[i].delay = originalGif.Delay[i]
|
||||
|
||||
ascii := flattenAscii(asciiCharSet, colored)
|
||||
|
||||
asciiArtSet[i] = strings.Join(ascii, "\n")
|
||||
|
||||
counter++
|
||||
percentage := int((float64(counter) / float64(len(originalGif.Image))) * 100)
|
||||
fmt.Printf("Generating ascii art... " + strconv.Itoa(percentage) + "%%\r")
|
||||
|
||||
wg.Done()
|
||||
|
||||
}(i, frame)
|
||||
|
||||
// Limit concurrent processes according to host's CPU count to avoid overwhelming memory
|
||||
if concurrentProcesses == hostCpuCount {
|
||||
wg.Wait()
|
||||
concurrentProcesses = 0
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
fmt.Printf(" \r")
|
||||
|
||||
// Save ascii art as .gif file before displaying it, if --save-gif flag is passed
|
||||
if saveGifPath != "" {
|
||||
|
||||
// Storing save path string before executing ascii art to gif conversion
|
||||
// This is done to avoid wasting time for invalid path errors
|
||||
|
||||
saveFileName, err := createSaveFileName(gifPath, urlImgName, "-ascii-art.gif")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fullPathName, err := getFullSavePath(saveFileName, saveGifPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't save file: %v", err)
|
||||
}
|
||||
|
||||
// Initializing some constants for gif. Done outside loop to save execution
|
||||
outGif := &gif.GIF{
|
||||
LoopCount: originalGif.LoopCount,
|
||||
}
|
||||
opts := gif.Options{
|
||||
NumColors: 256,
|
||||
Drawer: draw.FloydSteinberg,
|
||||
}
|
||||
|
||||
// Initializing slices for each ascii art image as well as delay
|
||||
var (
|
||||
palettedImageSlice = make([]*image.Paletted, len(gifFramesSlice))
|
||||
delaySlice = make([]int, len(gifFramesSlice))
|
||||
)
|
||||
|
||||
// For the purpose of displaying counter and limiting concurrent processes
|
||||
counter = 0
|
||||
concurrentProcesses = 0
|
||||
|
||||
fmt.Printf("Saving gif... 0%%\r")
|
||||
|
||||
// Multi-threaded loop to decrease execution time
|
||||
for i, gifFrame := range gifFramesSlice {
|
||||
|
||||
wg.Add(1)
|
||||
concurrentProcesses++
|
||||
|
||||
go func(i int, gifFrame GifFrame) {
|
||||
|
||||
img := originalGif.Image[i].SubImage(originalGif.Image[i].Rect)
|
||||
|
||||
tempImg, err := createGifFrameToSave(
|
||||
gifFrame.asciiCharSet,
|
||||
img,
|
||||
colored,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Following code takes tempImg as image.Image instance and converts it into *image.Paletted instance
|
||||
b := tempImg.Bounds()
|
||||
|
||||
palettedImg := image.NewPaletted(b, palette.Plan9[:opts.NumColors])
|
||||
|
||||
opts.Drawer.Draw(palettedImg, b, tempImg, image.Point{})
|
||||
|
||||
palettedImageSlice[i] = palettedImg
|
||||
delaySlice[i] = gifFrame.delay
|
||||
|
||||
counter++
|
||||
percentage := int((float64(counter) / float64(len(gifFramesSlice))) * 100)
|
||||
fmt.Printf("Saving gif... " + strconv.Itoa(percentage) + "%%\r")
|
||||
|
||||
wg.Done()
|
||||
|
||||
}(i, gifFrame)
|
||||
|
||||
// Limit concurrent processes according to host's CPU count to avoid overwhelming memory
|
||||
if concurrentProcesses == hostCpuCount {
|
||||
wg.Wait()
|
||||
concurrentProcesses = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
outGif.Image = palettedImageSlice
|
||||
outGif.Delay = delaySlice
|
||||
|
||||
gifFile, err := os.OpenFile(fullPathName, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't save file: %v", err)
|
||||
}
|
||||
defer gifFile.Close()
|
||||
|
||||
gif.EncodeAll(gifFile, outGif)
|
||||
|
||||
fmt.Printf(" \r")
|
||||
}
|
||||
|
||||
// Display the gif
|
||||
loopCount := 0
|
||||
for {
|
||||
for i, asciiFrame := range asciiArtSet {
|
||||
clearScreen()
|
||||
fmt.Println(asciiFrame)
|
||||
time.Sleep(time.Duration((time.Second * time.Duration(originalGif.Delay[i])) / 100))
|
||||
}
|
||||
|
||||
// If gif is infinite loop
|
||||
if originalGif.LoopCount == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
loopCount++
|
||||
if loopCount == originalGif.LoopCount {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
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 aic_package
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
_ "embed"
|
||||
|
||||
imgManip "github.com/TheZoraiz/ascii-image-converter/image_manipulation"
|
||||
"github.com/golang/freetype/truetype"
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
)
|
||||
|
||||
/*
|
||||
Unlike createImageToSave(), this function is optimized to maintain original image dimensions and shrink ascii
|
||||
art font size to match it. This allows for greater execution speed, which is necessary since a gif contains
|
||||
multiple images that need to be converted to ascii art, and the potential loss of ascii art quality (since
|
||||
large ascii art instances will shrink the font too much).
|
||||
|
||||
Furthermore, maintaining original gif's width and height also allows for gifs of smaller size.
|
||||
*/
|
||||
func createGifFrameToSave(asciiArt [][]imgManip.AsciiChar, img image.Image, colored bool) (image.Image, error) {
|
||||
|
||||
// Original image dimensions
|
||||
x := img.Bounds().Dx()
|
||||
y := img.Bounds().Dy()
|
||||
|
||||
// Ascii art dimensions
|
||||
asciiWidth := len(asciiArt[0])
|
||||
asciiHeight := len(asciiArt)
|
||||
|
||||
// Iterators to move pointer on the image to be made
|
||||
var xIter float64
|
||||
var yIter float64
|
||||
|
||||
var fontSize float64
|
||||
|
||||
// Conditions to alter resulting ascii gif dimensions according to ascii art dimensions
|
||||
if asciiWidth > asciiHeight*2 {
|
||||
yIter = float64(y) / float64(asciiHeight)
|
||||
|
||||
xIter = yIter / 2
|
||||
x = int(xIter * float64(asciiWidth))
|
||||
|
||||
fontSize = xIter
|
||||
|
||||
} else {
|
||||
xIter = float64(x) / float64(asciiWidth)
|
||||
|
||||
yIter = xIter * 2
|
||||
y = int(yIter * float64(asciiHeight))
|
||||
|
||||
fontSize = xIter
|
||||
}
|
||||
|
||||
// 10 extra pixels on both x and y-axis to have 5 pixels of padding on each side
|
||||
x += 10
|
||||
y += 10
|
||||
|
||||
tempImg := image.NewRGBA(image.Rect(0, 0, x, y))
|
||||
|
||||
dc := gg.NewContext(x, y)
|
||||
|
||||
// Set image background as black
|
||||
dc.SetRGB(0, 0, 0)
|
||||
dc.Clear()
|
||||
|
||||
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
|
||||
robotoBoldFontFace := truetype.NewFace(tempFont, &truetype.Options{Size: fontSize * 1.5})
|
||||
|
||||
dc.SetFontFace(robotoBoldFontFace)
|
||||
|
||||
// Font color of text on picture is white by default
|
||||
dc.SetColor(color.White)
|
||||
|
||||
// Pointer to track y-axis on the image frame
|
||||
yImgPointer := 5.0
|
||||
|
||||
// These nested loops print each character in asciArt 2D slice separately
|
||||
// so that their RGB colors can be maintained in the resulting image
|
||||
for _, line := range asciiArt {
|
||||
|
||||
// Pointer to track x-axis on the image frame
|
||||
xImgPointer := 5.0
|
||||
|
||||
for _, char := range line {
|
||||
|
||||
r := uint8(char.RgbValue[0])
|
||||
g := uint8(char.RgbValue[1])
|
||||
b := uint8(char.RgbValue[2])
|
||||
|
||||
if colored {
|
||||
// Simple put, dc.SetColor() sets color for EACH character before printing it
|
||||
dc.SetColor(color.RGBA{r, g, b, 255})
|
||||
}
|
||||
|
||||
dc.DrawStringWrapped(char.Simple, xImgPointer, yImgPointer, 0, 0, float64(x), 1.8, gg.AlignLeft)
|
||||
|
||||
// Incremet x-axis pointer character so new one can be printed after it
|
||||
// Set to the same constant as in line
|
||||
xImgPointer += xIter
|
||||
}
|
||||
|
||||
dc.DrawStringWrapped("\n", xImgPointer, yImgPointer, 0, 0, float64(x), 1.8, gg.AlignLeft)
|
||||
|
||||
// Incremet pointer for y axis after every line printed, so
|
||||
// new line can start at below the previous one on next iteration
|
||||
yImgPointer += yIter
|
||||
}
|
||||
|
||||
return dc.Image(), nil
|
||||
}
|
||||
Loading…
Reference in New Issue