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