code.pablomurad.com

goascii

Web app that renders images as colored ASCII art

tools.go raw
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
package main

import (
	"fmt"
	"image"
	"image/draw"
)

func hex(num uint) string {
	temp := []byte{'0', '0', '0', '0', '0', '0'}

	for counter := 0; num > 0; counter++ {
		n := uint8(num & 0xF)
		if n < 10 {
			temp[5-counter] = '0' + n
		} else {
			temp[5-counter] = ('A' - 10) + n
		}
		num >>= 4
	}

	return string(temp)
}

func gray(r, g, b uint8) uint8 {
	return r/5 + uint8(uint(g)*7/10) + b/10
}

func grayscale(img image.Image) {
	a := img.(*image.RGBA)
	for i := a.PixOffset(0, 0); i < len(a.Pix); i += 4 {
		g := gray(a.Pix[i], a.Pix[i+1], a.Pix[i+2])
		a.Pix[i] = g
		a.Pix[i+1] = g
		a.Pix[i+2] = g
	}
}

func monochrome(img image.Image) {
	a := img.(*image.RGBA)
	for i := a.PixOffset(0, 0); i < len(a.Pix); i += 4 {
		g := gray(a.Pix[i], a.Pix[i+1], a.Pix[i+2]) / 128 * 255
		a.Pix[i] = g
		a.Pix[i+1] = g
		a.Pix[i+2] = g
	}
}

func resize(original image.Image, w, h int) (image.Image, error) {
	src, ok := original.(*image.RGBA)
	if !ok {
		b := original.Bounds()
		src = image.NewRGBA(b)
		draw.Draw(src, b, original, b.Min, draw.Src)
	}

	width := src.Bounds().Dx()
	height := src.Bounds().Dy()

	if width < 1 || height < 1 {
		return image.Image(src), fmt.Errorf("image dimensions invalid -- %v, %v", width, height)
	}

	if h == 0 {
		h = int(float32(w) / (float32(width) / float32(height)))
	}
	if w < 1 || h < 1 {
		return image.Image(src), fmt.Errorf("resize values invalid -- %v, %v", w, h)
	}

	dst := image.NewRGBA(image.Rect(0, 0, w, h))

	xRatio := float32(width) / float32(w)
	yRatio := float32(height) / float32(h)

	if width > w {
		b := src.Bounds()
		i := src.PixOffset(0, 0)
		checklist := make([]bool, len(dst.Pix)>>2)

		for y := b.Min.Y; y < b.Max.Y; y++ {
			oy := int(float32(y) / yRatio)
			for x := b.Min.X; x < b.Max.X; x++ {
				ox := int(float32(x) / xRatio)
				o := dst.PixOffset(ox, oy)

				if !checklist[o>>2] {
					checklist[o>>2] = true
					dst.Pix[o+0] = src.Pix[i+0]
					dst.Pix[o+1] = src.Pix[i+1]
					dst.Pix[o+2] = src.Pix[i+2]
					dst.Pix[o+3] = src.Pix[i+3]
				} else {
					dst.Pix[o+0] = uint8((uint64(dst.Pix[o+0]) + uint64(src.Pix[i+0])) >> 1)
					dst.Pix[o+1] = uint8((uint64(dst.Pix[o+1]) + uint64(src.Pix[i+1])) >> 1)
					dst.Pix[o+2] = uint8((uint64(dst.Pix[o+2]) + uint64(src.Pix[i+2])) >> 1)
					dst.Pix[o+3] = uint8((uint64(dst.Pix[o+3]) + uint64(src.Pix[i+3])) >> 1)
				}

				i += 4
			}
		}
	} else {
		b := dst.Bounds()
		i := dst.PixOffset(0, 0)

		for y := b.Min.Y; y < b.Max.Y; y++ {
			oy := int(float32(y) * yRatio)
			for x := b.Min.X; x < b.Max.X; x++ {
				ox := int(float32(x) * xRatio)
				o := src.PixOffset(ox, oy)

				dst.Pix[i+0] = src.Pix[o+0]
				dst.Pix[i+1] = src.Pix[o+1]
				dst.Pix[i+2] = src.Pix[o+2]
				dst.Pix[i+3] = src.Pix[o+3]

				i += 4
			}
		}
	}

	return dst, nil
}