~dricottone/image2ascii

ref: dff12c38d66913f60ad6f66a8a6ac7f731c141bc image2ascii/ascii/ascii.go -rw-r--r-- 3.5 KiB
dff12c38Dominic Ricottone Adding toolchain and updating documentation 2 years ago
                                                                                
49547468 qeesung
5f88c33e qeesung
c5de7795 qeesung
5f88c33e qeesung
ea02edb8 qeesung
596b61e1 qeesung
3e0d95af qeesung
8ba26814 qeesung
4ad980ef qeesung
8ba26814 qeesung
4ad980ef qeesung
ea02edb8 qeesung
4ad980ef qeesung
8ba26814 qeesung
4ad980ef qeesung
ea02edb8 qeesung
32c6345e qeesung
5f88c33e qeesung
32c6345e qeesung
4ad980ef qeesung
5f88c33e qeesung
4ad980ef qeesung
5f88c33e qeesung
32c6345e qeesung
4ad980ef qeesung
ea02edb8 qeesung
596b61e1 qeesung
32c6345e qeesung
4ad980ef qeesung
c5de7795 qeesung
5f88c33e qeesung
4ad980ef qeesung
5f88c33e qeesung
4ad980ef qeesung
5f88c33e qeesung
4ad980ef qeesung
5f88c33e qeesung
c5de7795 qeesung
49547468 qeesung
596b61e1 qeesung
c5de7795 qeesung
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Package ascii can convert a image pixel to a raw char
// base on it's RGBA value, in another word, input a image pixel
// output a raw char ascii.
package ascii

import (
	"github.com/aybabtme/rgbterm"
	"image/color"
	"math"
	"reflect"
)

// CharPixel is converted pixel ascii
type CharPixel struct {
	Char byte
	R    uint8
	G    uint8
	B    uint8
	A    uint8
}

// Options convert pixel to raw char
type Options struct {
	Pixels   []byte
	Reversed bool
	Colored  bool
}

// DefaultOptions that contains the default pixels
var DefaultOptions = Options{
	Pixels:   []byte(" .,:;i1tfLCG08@"),
	Reversed: false,
	Colored:  true,
}

// NewOptions create a new convert option
func NewOptions() Options {
	newOptions := Options{}
	newOptions.mergeOptions(&DefaultOptions)
	return newOptions
}

// mergeOptions merge two options
func (options *Options) mergeOptions(newOptions *Options) {
	options.Pixels = append([]byte{}, newOptions.Pixels...)
	options.Reversed = newOptions.Reversed
	options.Colored = newOptions.Colored
}

// NewPixelConverter create a new pixel converter
func NewPixelConverter() PixelConverter {
	return PixelASCIIConverter{}
}

// PixelConverter define the convert pixel operation
type PixelConverter interface {
	ConvertPixelToASCII(pixel color.Color, options *Options) string
	ConvertPixelToPixelASCII(pixel color.Color, options *Options) CharPixel
}

// PixelASCIIConverter responsible for pixel ascii conversion
type PixelASCIIConverter struct {
}

// ConvertPixelToPixelASCII convert a image pixel to CharPixel
func (converter PixelASCIIConverter) ConvertPixelToPixelASCII(pixel color.Color, options *Options) CharPixel {
	convertOptions := NewOptions()
	convertOptions.mergeOptions(options)

	if convertOptions.Reversed {
		convertOptions.Pixels = converter.reverse(convertOptions.Pixels)
	}

	r := reflect.ValueOf(pixel).FieldByName("R").Uint()
	g := reflect.ValueOf(pixel).FieldByName("G").Uint()
	b := reflect.ValueOf(pixel).FieldByName("B").Uint()
	a := reflect.ValueOf(pixel).FieldByName("A").Uint()
	value := converter.intensity(r, g, b, a)

	// Choose the char
	precision := float64(255 * 3 / (len(convertOptions.Pixels) - 1))
	rawChar := convertOptions.Pixels[converter.roundValue(float64(value)/precision)]
	return CharPixel{
		Char: rawChar,
		R:    uint8(r),
		G:    uint8(g),
		B:    uint8(b),
		A:    uint8(a),
	}
}

// ConvertPixelToASCII converts a pixel to a ASCII char string
func (converter PixelASCIIConverter) ConvertPixelToASCII(pixel color.Color, options *Options) string {
	convertOptions := NewOptions()
	convertOptions.mergeOptions(options)

	pixelASCII := converter.ConvertPixelToPixelASCII(pixel, options)
	rawChar, r, g, b := pixelASCII.Char, pixelASCII.R, pixelASCII.G, pixelASCII.B
	if convertOptions.Colored {
		return converter.decorateWithColor(r, g, b, rawChar)
	}
	return string([]byte{rawChar})
}

func (converter PixelASCIIConverter) roundValue(value float64) int {
	return int(math.Floor(value + 0.5))
}

func (converter PixelASCIIConverter) reverse(numbers []byte) []byte {
	for i := 0; i < len(numbers)/2; i++ {
		j := len(numbers) - i - 1
		numbers[i], numbers[j] = numbers[j], numbers[i]
	}
	return numbers
}

func (converter PixelASCIIConverter) intensity(r, g, b, a uint64) uint64 {
	return (r + g + b) * a / 255
}

// decorateWithColor decorate the raw char with the color base on r,g,b value
func (converter PixelASCIIConverter) decorateWithColor(r, g, b uint8, rawChar byte) string {
	coloredChar := rgbterm.FgString(string([]byte{rawChar}), uint8(r), uint8(g), uint8(b))
	return coloredChar
}