1package color
2
3import (
4	"image/color"
5	"math"
6)
7
8func fromFloat(v, gamma float64) uint32 {
9	x := math.Pow(v, gamma)
10	switch {
11	case x >= 1:
12		return 0xffff
13	case x <= 0:
14		return 0
15	default:
16		return uint32(x * 0xffff)
17	}
18}
19
20func toFloat(v uint32, gamma float64) float64 {
21	return math.Pow(float64(v)/0xffff, gamma)
22}
23
24// Gray1 represents an 1-bit monochrome bitmap color.
25type Gray1 struct {
26	Y bool
27}
28
29// RGBA implements color.Color interface's method.
30func (c Gray1) RGBA() (r, g, b, a uint32) {
31	if c.Y {
32		return 0xffff, 0xffff, 0xffff, 0xffff
33	}
34	return 0, 0, 0, 0xffff
35}
36
37func gray1Model(c color.Color) color.Color {
38	if _, ok := c.(Gray1); ok {
39		return c
40	}
41	r, g, b, _ := c.RGBA()
42	y := (299*r + 587*g + 114*b + 500) / 1000
43	return Gray1{y >= 0x8000}
44}
45
46// Gray32 represents a 32-bit float grayscale color.
47type Gray32 struct {
48	Y float32
49}
50
51// RGBA implements color.Color interface's method.
52func (c Gray32) RGBA() (r, g, b, a uint32) {
53	const gamma = 1.0 / 2.2
54	y := fromFloat(float64(c.Y), gamma)
55	return y, y, y, 0xffff
56}
57
58func gray32Model(c color.Color) color.Color {
59	if _, ok := c.(Gray32); ok {
60		return c
61	}
62	r, g, b, _ := c.RGBA()
63	y := (299*r + 587*g + 114*b + 500) / 1000
64	const gamma = 2.2
65	return Gray32{float32(toFloat(y, gamma))}
66}
67
68type NGrayA struct {
69	Y uint8
70	A uint8
71}
72
73// RGBA implements color.Color interface's method.
74func (c NGrayA) RGBA() (uint32, uint32, uint32, uint32) {
75	y := uint32(c.Y) * 0x101
76	if c.A == 0xff {
77		return y, y, y, 0xffff
78	}
79	if c.A == 0 {
80		return 0, 0, 0, 0
81	}
82	a := uint32(c.A) * 0x101
83	y = y * a / 0xffff
84	return y, y, y, a
85}
86
87func nGrayAModel(c color.Color) color.Color {
88	if _, ok := c.(NGrayA64); ok {
89		return c
90	}
91	r, g, b, a := c.RGBA()
92	if a == 0 {
93		return NGrayA{0, 0}
94	}
95	y := (299*r + 587*g + 114*b + 500) / 1000
96	if a == 0xffff {
97		return NGrayA{uint8(y >> 8), 0xff}
98	}
99	y = (y * 0xffff) / a
100	return NGrayA{uint8(y >> 8), uint8(a >> 8)}
101}
102
103type NGrayA32 struct {
104	Y uint16
105	A uint16
106}
107
108// RGBA implements color.Color interface's method.
109func (c NGrayA32) RGBA() (uint32, uint32, uint32, uint32) {
110	y := uint32(c.Y)
111	if c.A == 0xffff {
112		return y, y, y, 0xffff
113	}
114	if c.A == 0 {
115		return 0, 0, 0, 0
116	}
117	a := uint32(c.A)
118	y = y * a / 0xffff
119	return y, y, y, a
120}
121
122func nGrayA32Model(c color.Color) color.Color {
123	if _, ok := c.(NGrayA64); ok {
124		return c
125	}
126	r, g, b, a := c.RGBA()
127	if a == 0 {
128		return NGrayA32{0, 0}
129	}
130	y := (299*r + 587*g + 114*b + 500) / 1000
131	if a == 0xffff {
132		return NGrayA32{uint16(y), 0xffff}
133	}
134	y = (y * 0xffff) / a
135	return NGrayA32{uint16(y), uint16(a)}
136}
137
138type NGrayA64 struct {
139	Y float32
140	A float32
141}
142
143// RGBA implements color.Color interface's method.
144func (c NGrayA64) RGBA() (uint32, uint32, uint32, uint32) {
145	y := fromFloat(float64(c.Y), 1.0/2.2)
146	switch {
147	case c.A >= 1:
148		return y, y, y, 0xffff
149	case c.A <= 0:
150		return 0, 0, 0, 0
151	}
152	a := uint32(c.A * 0xffff)
153	y = y * a / 0xffff
154	return y, y, y, a
155}
156
157func nGrayA64Model(c color.Color) color.Color {
158	if _, ok := c.(NGrayA64); ok {
159		return c
160	}
161	r, g, b, a := c.RGBA()
162	if a == 0 {
163		return NGrayA64{0, 0}
164	}
165	y := (299*r + 587*g + 114*b + 500) / 1000
166	x := float32(toFloat(y, 2.2))
167	if a == 0xffff {
168		return NGrayA64{x, 1}
169	}
170	xa := float32(a) / 0xffff
171	return NGrayA64{x / xa, xa}
172}
173
174type NRGBA128 struct {
175	R, G, B, A float32
176}
177
178// RGBA implements color.Color interface's method.
179func (c NRGBA128) RGBA() (uint32, uint32, uint32, uint32) {
180	const gamma = 1.0 / 2.2
181	r := fromFloat(float64(c.R), gamma)
182	g := fromFloat(float64(c.G), gamma)
183	b := fromFloat(float64(c.B), gamma)
184	switch {
185	case c.A >= 1:
186		return r, g, b, 0xffff
187	case c.A <= 0:
188		return 0, 0, 0, 0
189	}
190	a := uint32(c.A * 0xffff)
191	r = r * a / 0xffff
192	g = g * a / 0xffff
193	b = b * a / 0xffff
194	return r, g, b, a
195}
196
197func nRGBA128Model(c color.Color) color.Color {
198	if _, ok := c.(NRGBA128); ok {
199		return c
200	}
201	r, g, b, a := c.RGBA()
202	const gamma = 2.2
203	fr := float32(toFloat(r, gamma))
204	fg := float32(toFloat(g, gamma))
205	fb := float32(toFloat(b, gamma))
206	switch {
207	case a >= 0xffff:
208		return NRGBA128{fr, fg, fb, 1}
209	case a == 0:
210		return NRGBA128{}
211	}
212	fa := 0xffff / float32(a)
213	fr *= fa
214	fg *= fa
215	fb *= fa
216	return NRGBA128{fr, fg, fb, float32(a) / 0xffff}
217}
218
219// NCMYKA represents a non-alpha-premultiplied CMYK color, having 8 bits for each of cyan,
220// magenta, yellow, black and alpha.
221// NCMYKA is different from color.CMYK, CMYK is inverted value.
222//
223// It is not associated with any particular color profile.
224type NCMYKA struct {
225	C, M, Y, K, A uint8
226}
227
228// RGBA implements color.Color interface's method.
229func (c NCMYKA) RGBA() (uint32, uint32, uint32, uint32) {
230	w := uint32(c.K) * 0x10201
231	r := uint32(c.C) * w / 0xffff
232	g := uint32(c.M) * w / 0xffff
233	b := uint32(c.Y) * w / 0xffff
234	if c.A == 0xff {
235		return r, g, b, 0xffff
236	}
237	if c.A == 0 {
238		return 0, 0, 0, 0
239	}
240
241	a := uint32(c.A) * 0x101
242	r = r * a / 0xffff
243	g = g * a / 0xffff
244	b = b * a / 0xffff
245	return r, g, b, a
246}
247
248func nCMYKAModel(c color.Color) color.Color {
249	if _, ok := c.(NCMYKA); ok {
250		return c
251	}
252	r, g, b, a := c.RGBA()
253	cc, mm, yy, kk := color.RGBToCMYK(uint8(r>>8), uint8(g>>8), uint8(b>>8))
254	cc = uint8((uint32(cc) * 0xffff) / a)
255	mm = uint8((uint32(mm) * 0xffff) / a)
256	yy = uint8((uint32(yy) * 0xffff) / a)
257	kk = uint8((uint32(kk) * 0xffff) / a)
258	return NCMYKA{255 - cc, 255 - mm, 255 - yy, 255 - kk, uint8(a >> 8)}
259}
260
261type NCMYKA80 struct {
262	C, M, Y, K, A uint16
263}
264
265// RGBA implements color.Color interface's method.
266func (c NCMYKA80) RGBA() (uint32, uint32, uint32, uint32) {
267	w := uint32(c.K)
268	r := uint32(c.C) * w / 0xffff
269	g := uint32(c.M) * w / 0xffff
270	b := uint32(c.Y) * w / 0xffff
271	if c.A == 0xffff {
272		return r, g, b, 0xffff
273	}
274	if c.A == 0 {
275		return 0, 0, 0, 0
276	}
277
278	a := uint32(c.A)
279	r = r * a / 0xffff
280	g = g * a / 0xffff
281	b = b * a / 0xffff
282	return r, g, b, a
283}
284
285func nCMYKA80Model(c color.Color) color.Color {
286	if _, ok := c.(NCMYKA); ok {
287		return c
288	}
289	r, g, b, a := c.RGBA()
290	if a == 0 {
291		return NCMYKA80{0xffff, 0xffff, 0xffff, 0xffff, 0}
292	}
293	w := r
294	if w < g {
295		w = g
296	}
297	if w < b {
298		w = b
299	}
300	if w == 0 {
301		return NCMYKA80{0xffff, 0xffff, 0xffff, 0xffff, uint16(a)}
302	}
303	cc := (w - r) * 0xffff / w
304	mm := (w - g) * 0xffff / w
305	yy := (w - b) * 0xffff / w
306	kk := 0xffff - w
307	if a == 0xffff {
308		return NCMYKA80{uint16(0xffff - cc), uint16(0xffff - mm), uint16(0xffff - yy), uint16(0xffff - kk), 0xffff}
309	}
310	cc = (cc * 0xffff) / a
311	mm = (mm * 0xffff) / a
312	yy = (yy * 0xffff) / a
313	kk = (kk * 0xffff) / a
314	return NCMYKA80{uint16(0xffff - cc), uint16(0xffff - mm), uint16(0xffff - yy), uint16(0xffff - kk), uint16(a)}
315}
316
317// These are color model.
318var (
319	Gray1Model    = color.ModelFunc(gray1Model)
320	NGrayAModel   = color.ModelFunc(nGrayAModel)
321	Gray32Model   = color.ModelFunc(gray32Model)
322	NGrayA32Model = color.ModelFunc(nGrayA32Model)
323	NGrayA64Model = color.ModelFunc(nGrayA64Model)
324	NRGBA128Model = color.ModelFunc(nRGBA128Model)
325	NCMYKAModel   = color.ModelFunc(nCMYKAModel)
326	NCMYKA80Model = color.ModelFunc(nCMYKA80Model)
327)
328