summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Benau/tgsconverter/libtgsconverter/quantize_bucket.go
blob: 1f00c6850ec9d784e673b863d89aae4e3820a694 (plain) (blame)
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
package libtgsconverter

import "image/color"

type colorAxis uint8

// Color axis constants
const (
	red colorAxis = iota
	green
	blue
)

type colorPriority struct {
	p uint32
	color.RGBA
}

func (c colorPriority) axis(span colorAxis) uint8 {
	switch span {
	case red:
		return c.R
	case green:
		return c.G
	default:
		return c.B
	}
}

type colorBucket []colorPriority

func (cb colorBucket) partition() (colorBucket, colorBucket) {
	mean, span := cb.span()
	left, right := 0, len(cb)-1
	for left < right {
		cb[left], cb[right] = cb[right], cb[left]
		for cb[left].axis(span) < mean && left < right {
			left++
		}
		for cb[right].axis(span) >= mean && left < right {
			right--
		}
	}
	if left == 0 {
		return cb[:1], cb[1:]
	}
	if left == len(cb)-1 {
		return cb[:len(cb)-1], cb[len(cb)-1:]
	}
	return cb[:left], cb[left:]
}

func (cb colorBucket) mean() color.RGBA {
	var r, g, b uint64
	var p uint64
	for _, c := range cb {
		p += uint64(c.p)
		r += uint64(c.R) * uint64(c.p)
		g += uint64(c.G) * uint64(c.p)
		b += uint64(c.B) * uint64(c.p)
	}
	return color.RGBA{uint8(r / p), uint8(g / p), uint8(b / p), 255}
}

type constraint struct {
	min  uint8
	max  uint8
	vals [256]uint64
}

func (c *constraint) update(index uint8, p uint32) {
	if index < c.min {
		c.min = index
	}
	if index > c.max {
		c.max = index
	}
	c.vals[index] += uint64(p)
}

func (c *constraint) span() uint8 {
	return c.max - c.min
}

func (cb colorBucket) span() (uint8, colorAxis) {
	var R, G, B constraint
	R.min = 255
	G.min = 255
	B.min = 255
	var p uint64
	for _, c := range cb {
		R.update(c.R, c.p)
		G.update(c.G, c.p)
		B.update(c.B, c.p)
		p += uint64(c.p)
	}
	var toCount *constraint
	var span colorAxis
	if R.span() > G.span() && R.span() > B.span() {
		span = red
		toCount = &R
	} else if G.span() > B.span() {
		span = green
		toCount = &G
	} else {
		span = blue
		toCount = &B
	}
	var counted uint64
	var i int
	var c uint64
	for i, c = range toCount.vals {
		if counted > p/2 || counted+c == p {
			break
		}
		counted += c
	}
	return uint8(i), span
}