~dricottone/image2ascii

ref: fe3f9eba02b746cf4238e46e71fe5e0a54ad5bdd image2ascii/ascii/ascii.go -rw-r--r-- 3.5 KiB
fe3f9eba — qeesung fixed the go lint issue 6 years ago
                                                                                
49547468 qeesung
5f88c33e qeesung
c5de7795 qeesung
5f88c33e qeesung
fe3f9eba qeesung
596b61e1 qeesung
3e0d95af qeesung
8ba26814 qeesung
4ad980ef qeesung
8ba26814 qeesung
4ad980ef qeesung
596b61e1 qeesung
4ad980ef qeesung
8ba26814 qeesung
4ad980ef qeesung
596b61e1 qeesung
32c6345e qeesung
5f88c33e qeesung
32c6345e qeesung
4ad980ef qeesung
5f88c33e qeesung
4ad980ef qeesung
5f88c33e qeesung
32c6345e qeesung
4ad980ef 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"
)

// PixelASCII is converted pixel ascii
type PixelASCII 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) PixelASCII
}

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

// ConvertPixelToPixelASCII convert a image pixel to PixelASCII
func (converter PixelASCIIConverter) ConvertPixelToPixelASCII(pixel color.Color, options *Options) PixelASCII {
	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 PixelASCII{
		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
}