1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package iconvg
6
7import (
8	"image/color"
9)
10
11func validAlphaPremulColor(c color.RGBA) bool {
12	return c.R <= c.A && c.G <= c.A && c.B <= c.A
13}
14
15// ColorType distinguishes types of Colors.
16type ColorType uint8
17
18const (
19	// ColorTypeRGBA is a direct RGBA color.
20	ColorTypeRGBA ColorType = iota
21
22	// ColorTypePaletteIndex is an indirect color, indexing the custom palette.
23	ColorTypePaletteIndex
24
25	// ColorTypeCReg is an indirect color, indexing the CREG color registers.
26	ColorTypeCReg
27
28	// ColorTypeBlend is an indirect color, blending two other colors.
29	ColorTypeBlend
30)
31
32// Color is an IconVG color, whose RGBA values can depend on context. Some
33// Colors are direct RGBA values. Other Colors are indirect, referring to an
34// index of the custom palette, a color register of the decoder virtual
35// machine, or a blend of two other Colors.
36//
37// See the "Colors" section in the package documentation for details.
38type Color struct {
39	typ  ColorType
40	data color.RGBA
41}
42
43func (c Color) rgba() color.RGBA         { return c.data }
44func (c Color) paletteIndex() uint8      { return c.data.R }
45func (c Color) cReg() uint8              { return c.data.R }
46func (c Color) blend() (t, c0, c1 uint8) { return c.data.R, c.data.G, c.data.B }
47
48// Resolve resolves the Color's RGBA value, given its context: the custom
49// palette and the color registers of the decoder virtual machine.
50func (c Color) Resolve(pal *Palette, cReg *[64]color.RGBA) color.RGBA {
51	switch c.typ {
52	case ColorTypeRGBA:
53		return c.rgba()
54	case ColorTypePaletteIndex:
55		return pal[c.paletteIndex()&0x3f]
56	case ColorTypeCReg:
57		return cReg[c.cReg()&0x3f]
58	}
59	t, c0, c1 := c.blend()
60	p, q := uint32(255-t), uint32(t)
61	rgba0 := decodeColor1(c0).Resolve(pal, cReg)
62	rgba1 := decodeColor1(c1).Resolve(pal, cReg)
63	return color.RGBA{
64		uint8(((p * uint32(rgba0.R)) + q*uint32(rgba1.R) + 128) / 255),
65		uint8(((p * uint32(rgba0.G)) + q*uint32(rgba1.G) + 128) / 255),
66		uint8(((p * uint32(rgba0.B)) + q*uint32(rgba1.B) + 128) / 255),
67		uint8(((p * uint32(rgba0.A)) + q*uint32(rgba1.A) + 128) / 255),
68	}
69}
70
71// RGBAColor returns a direct Color.
72func RGBAColor(c color.RGBA) Color { return Color{ColorTypeRGBA, c} }
73
74// PaletteIndexColor returns an indirect Color referring to an index of the
75// custom palette.
76func PaletteIndexColor(i uint8) Color { return Color{ColorTypePaletteIndex, color.RGBA{R: i & 0x3f}} }
77
78// CRegColor returns an indirect Color referring to a color register of the
79// decoder virtual machine.
80func CRegColor(i uint8) Color { return Color{ColorTypeCReg, color.RGBA{R: i & 0x3f}} }
81
82// BlendColor returns an indirect Color that blends two other Colors. Those two
83// other Colors must both be encodable as a 1 byte color.
84//
85// To blend a Color that is not encodable as a 1 byte color, first load that
86// Color into a CREG color register, then call CRegColor to produce a Color
87// that is encodable as a 1 byte color. See testdata/favicon.ivg for an
88// example.
89//
90// See the "Colors" section in the package documentation for details.
91func BlendColor(t, c0, c1 uint8) Color { return Color{ColorTypeBlend, color.RGBA{R: t, G: c0, B: c1}} }
92
93func decodeColor1(x byte) Color {
94	if x >= 0x80 {
95		if x >= 0xc0 {
96			return CRegColor(x)
97		} else {
98			return PaletteIndexColor(x)
99		}
100	}
101	if x >= 125 {
102		switch x - 125 {
103		case 0:
104			return RGBAColor(color.RGBA{0xc0, 0xc0, 0xc0, 0xc0})
105		case 1:
106			return RGBAColor(color.RGBA{0x80, 0x80, 0x80, 0x80})
107		case 2:
108			return RGBAColor(color.RGBA{0x00, 0x00, 0x00, 0x00})
109		}
110	}
111	blue := dc1Table[x%5]
112	x = x / 5
113	green := dc1Table[x%5]
114	x = x / 5
115	red := dc1Table[x]
116	return RGBAColor(color.RGBA{red, green, blue, 0xff})
117}
118
119var dc1Table = [5]byte{0x00, 0x40, 0x80, 0xc0, 0xff}
120
121func is1(u uint8) bool { return u&0x3f == 0 || u == 0xff }
122
123func encodeColor1(c Color) (x byte, ok bool) {
124	switch c.typ {
125	case ColorTypeRGBA:
126		if c.data.A != 0xff {
127			switch c.data {
128			case color.RGBA{0x00, 0x00, 0x00, 0x00}:
129				return 127, true
130			case color.RGBA{0x80, 0x80, 0x80, 0x80}:
131				return 126, true
132			case color.RGBA{0xc0, 0xc0, 0xc0, 0xc0}:
133				return 125, true
134			}
135		} else if is1(c.data.R) && is1(c.data.G) && is1(c.data.B) && is1(c.data.A) {
136			r := c.data.R / 0x3f
137			g := c.data.G / 0x3f
138			b := c.data.B / 0x3f
139			return 25*r + 5*g + b, true
140		}
141	case ColorTypePaletteIndex:
142		return c.data.R | 0x80, true
143	case ColorTypeCReg:
144		return c.data.R | 0xc0, true
145	}
146	return 0, false
147}
148
149func is2(u uint8) bool { return u%0x11 == 0 }
150
151func encodeColor2(c Color) (x [2]byte, ok bool) {
152	if c.typ == ColorTypeRGBA && is2(c.data.R) && is2(c.data.G) && is2(c.data.B) && is2(c.data.A) {
153		return [2]byte{
154			(c.data.R/0x11)<<4 | (c.data.G / 0x11),
155			(c.data.B/0x11)<<4 | (c.data.A / 0x11),
156		}, true
157	}
158	return [2]byte{}, false
159}
160
161func encodeColor3Direct(c Color) (x [3]byte, ok bool) {
162	if c.typ == ColorTypeRGBA && c.data.A == 0xff {
163		return [3]byte{c.data.R, c.data.G, c.data.B}, true
164	}
165	return [3]byte{}, false
166}
167
168func encodeColor4(c Color) (x [4]byte, ok bool) {
169	if c.typ == ColorTypeRGBA {
170		return [4]byte{c.data.R, c.data.G, c.data.B, c.data.A}, true
171	}
172	return [4]byte{}, false
173}
174
175func encodeColor3Indirect(c Color) (x [3]byte, ok bool) {
176	if c.typ == ColorTypeBlend {
177		return [3]byte{c.data.R, c.data.G, c.data.B}, true
178	}
179	return [3]byte{}, false
180}
181