| // 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 |
| } |