Restored accidental file misplacement

This commit is contained in:
Zoraiz 2021-07-24 02:56:27 +05:00
parent ed3dfa1ed5
commit 3f6d1840c5
4 changed files with 5 additions and 392 deletions

View File

@ -129,10 +129,10 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
wg.Wait()
fmt.Printf(" \r")
// Save ascii art as .gif image before displaying it, if --save-gif flag is passed
// Save ascii art as .gif file before displaying it, if --save-gif flag is passed
if saveGifPath != "" {
// Storing save path string before executing the concurrent ascii art to gif conversion
// 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")

View File

@ -78,11 +78,12 @@ func createGifFrameToSave(asciiArt [][]imgManip.AsciiChar, img image.Image, colo
dc := gg.NewContext(x, y)
dc.DrawImage(tempImg, 0, 0)
// Set image background as black
dc.SetRGBA(0, 0, 0, 0)
dc.SetRGB(0, 0, 0)
dc.Clear()
dc.DrawImage(tempImg, 0, 0)
// Load embedded font
tempFont, err := truetype.Parse(embeddedFontFile)
if err != nil {

View File

@ -1,252 +0,0 @@
/*
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
}

View File

@ -1,136 +0,0 @@
/*
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
}