blob: b2fa7d39c90ae0c014fe511bb73b1131812ca7ca [file] [log] [blame]
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package iconvg
import (
"image/color"
)
func validAlphaPremulColor(c color.RGBA) bool {
return c.R <= c.A && c.G <= c.A && c.B <= c.A
}
// ColorType distinguishes types of Colors.
type ColorType uint8
const (
// ColorTypeRGBA is a direct RGBA color.
ColorTypeRGBA ColorType = iota
// ColorTypePaletteIndex is an indirect color, indexing the custom palette.
ColorTypePaletteIndex
// ColorTypeCReg is an indirect color, indexing the CREG color registers.
ColorTypeCReg
// ColorTypeBlend is an indirect color, blending two other colors.
ColorTypeBlend
)
// Color is an IconVG color, whose RGBA values can depend on context. Some
// Colors are direct RGBA values. Other Colors are indirect, referring to an
// index of the custom palette, a color register of the decoder virtual
// machine, or a blend of two other Colors.
//
// See the "Colors" section in the package documentation for details.
type Color struct {
typ ColorType
data color.RGBA
}
func (c Color) rgba() color.RGBA { return c.data }
func (c Color) paletteIndex() uint8 { return c.data.R }
func (c Color) cReg() uint8 { return c.data.R }
func (c Color) blend() (t, c0, c1 uint8) { return c.data.R, c.data.G, c.data.B }
// Resolve resolves the Color's RGBA value, given its context: the custom
// palette and the color registers of the decoder virtual machine.
func (c Color) Resolve(pal *Palette, cReg *[64]color.RGBA) color.RGBA {
switch c.typ {
case ColorTypeRGBA:
return c.rgba()
case ColorTypePaletteIndex:
return pal[c.paletteIndex()&0x3f]
case ColorTypeCReg:
return cReg[c.cReg()&0x3f]
}
t, c0, c1 := c.blend()
p, q := uint32(255-t), uint32(t)
rgba0 := decodeColor1(c0).Resolve(pal, cReg)
rgba1 := decodeColor1(c1).Resolve(pal, cReg)
return color.RGBA{
uint8(((p * uint32(rgba0.R)) + q*uint32(rgba1.R) + 128) / 255),
uint8(((p * uint32(rgba0.G)) + q*uint32(rgba1.G) + 128) / 255),
uint8(((p * uint32(rgba0.B)) + q*uint32(rgba1.B) + 128) / 255),
uint8(((p * uint32(rgba0.A)) + q*uint32(rgba1.A) + 128) / 255),
}
}
// RGBAColor returns a direct Color.
func RGBAColor(c color.RGBA) Color { return Color{ColorTypeRGBA, c} }
// PaletteIndexColor returns an indirect Color referring to an index of the
// custom palette.
func PaletteIndexColor(i uint8) Color { return Color{ColorTypePaletteIndex, color.RGBA{R: i & 0x3f}} }
// CRegColor returns an indirect Color referring to a color register of the
// decoder virtual machine.
func CRegColor(i uint8) Color { return Color{ColorTypeCReg, color.RGBA{R: i & 0x3f}} }
// BlendColor returns an indirect Color that blends two other Colors. Those two
// other Colors must both be encodable as a 1 byte color.
//
// To blend a Color that is not encodable as a 1 byte color, first load that
// Color into a CREG color register, then call CRegColor to produce a Color
// that is encodable as a 1 byte color. See testdata/favicon.ivg for an
// example.
//
// See the "Colors" section in the package documentation for details.
func BlendColor(t, c0, c1 uint8) Color { return Color{ColorTypeBlend, color.RGBA{R: t, G: c0, B: c1}} }
func decodeColor1(x byte) Color {
if x >= 0x80 {
if x >= 0xc0 {
return CRegColor(x)
} else {
return PaletteIndexColor(x)
}
}
if x >= 125 {
switch x - 125 {
case 0:
return RGBAColor(color.RGBA{0xc0, 0xc0, 0xc0, 0xc0})
case 1:
return RGBAColor(color.RGBA{0x80, 0x80, 0x80, 0x80})
case 2:
return RGBAColor(color.RGBA{0x00, 0x00, 0x00, 0x00})
}
}
blue := dc1Table[x%5]
x = x / 5
green := dc1Table[x%5]
x = x / 5
red := dc1Table[x]
return RGBAColor(color.RGBA{red, green, blue, 0xff})
}
var dc1Table = [5]byte{0x00, 0x40, 0x80, 0xc0, 0xff}
func is1(u uint8) bool { return u&0x3f == 0 || u == 0xff }
func encodeColor1(c Color) (x byte, ok bool) {
switch c.typ {
case ColorTypeRGBA:
if c.data.A != 0xff {
switch c.data {
case color.RGBA{0x00, 0x00, 0x00, 0x00}:
return 127, true
case color.RGBA{0x80, 0x80, 0x80, 0x80}:
return 126, true
case color.RGBA{0xc0, 0xc0, 0xc0, 0xc0}:
return 125, true
}
} else if is1(c.data.R) && is1(c.data.G) && is1(c.data.B) && is1(c.data.A) {
r := c.data.R / 0x3f
g := c.data.G / 0x3f
b := c.data.B / 0x3f
return 25*r + 5*g + b, true
}
case ColorTypePaletteIndex:
return c.data.R | 0x80, true
case ColorTypeCReg:
return c.data.R | 0xc0, true
}
return 0, false
}
func is2(u uint8) bool { return u%0x11 == 0 }
func encodeColor2(c Color) (x [2]byte, ok bool) {
if c.typ == ColorTypeRGBA && is2(c.data.R) && is2(c.data.G) && is2(c.data.B) && is2(c.data.A) {
return [2]byte{
(c.data.R/0x11)<<4 | (c.data.G / 0x11),
(c.data.B/0x11)<<4 | (c.data.A / 0x11),
}, true
}
return [2]byte{}, false
}
func encodeColor3Direct(c Color) (x [3]byte, ok bool) {
if c.typ == ColorTypeRGBA && c.data.A == 0xff {
return [3]byte{c.data.R, c.data.G, c.data.B}, true
}
return [3]byte{}, false
}
func encodeColor4(c Color) (x [4]byte, ok bool) {
if c.typ == ColorTypeRGBA {
return [4]byte{c.data.R, c.data.G, c.data.B, c.data.A}, true
}
return [4]byte{}, false
}
func encodeColor3Indirect(c Color) (x [3]byte, ok bool) {
if c.typ == ColorTypeBlend {
return [3]byte{c.data.R, c.data.G, c.data.B}, true
}
return [3]byte{}, false
}