Added APT repo

This commit is contained in:
Zoraiz 2021-07-24 02:53:47 +05:00
parent a421952bfe
commit ed3dfa1ed5
4 changed files with 449 additions and 13 deletions

View File

@ -13,11 +13,25 @@ builds:
- windows - windows
goarch: goarch:
# Architectures for executable binary generation
- amd64 - amd64
- arm64 - arm64
- arm - arm
- 386 - 386
# Specifying all architectures for .deb and .rpm generation
# - 386
# - amd64
# - arm
# - arm64
# - ppc64
# - ppc64le
# - mips
# - mipsle
# - mips64
# - mips64le
archives: archives:
- -
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
@ -50,3 +64,25 @@ changelog:
exclude: exclude:
- '^docs:' - '^docs:'
- '^test:' - '^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

View File

@ -1,3 +1,5 @@
# 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)
@ -21,6 +23,7 @@ Input formats currently supported:
## Table of Contents ## Table of Contents
- [Installation](#installation) - [Installation](#installation)
* [Ubuntu / Ubuntu-based](#ubuntu-or-ubuntu-based-distros)
* [Snap](#snap) * [Snap](#snap)
* [Go](#go) * [Go](#go)
* [Linux (binaries)](#linux) * [Linux (binaries)](#linux)
@ -34,11 +37,25 @@ Input formats currently supported:
## Installation ## 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 ### Snap
You can download through 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 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/ 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 ### 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: 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 "view advanced system settings" (Control Panel) * In Search, search for and then select: System (Control Panel)
* Click `Environment Variables`. * Click the Advanced System settings link.
* 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` on the right side 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.
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> <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. 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"> <p align="center">
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/save.gif"> <img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/save.gif">
</p> </p>

252
convert_gif.go Normal file
View File

@ -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
}

136
create_ascii_gif.go Normal file
View File

@ -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
}