github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/gift/pixels.go (about) 1 package gift 2 3 import ( 4 "image" 5 "image/color" 6 "image/draw" 7 ) 8 9 type pixel struct { 10 R, G, B, A float32 11 } 12 13 type imageType int 14 15 const ( 16 itGeneric imageType = iota 17 itNRGBA 18 itNRGBA64 19 itRGBA 20 itRGBA64 21 itYCbCr 22 itGray 23 itGray16 24 itPaletted 25 ) 26 27 type pixelGetter struct { 28 imgType imageType 29 imgBounds image.Rectangle 30 imgGeneric image.Image 31 imgNRGBA *image.NRGBA 32 imgNRGBA64 *image.NRGBA64 33 imgRGBA *image.RGBA 34 imgRGBA64 *image.RGBA64 35 imgYCbCr *image.YCbCr 36 imgGray *image.Gray 37 imgGray16 *image.Gray16 38 imgPaletted *image.Paletted 39 imgPalette []pixel 40 } 41 42 func newPixelGetter(img image.Image) (p *pixelGetter) { 43 switch img := img.(type) { 44 case *image.NRGBA: 45 p = &pixelGetter{ 46 imgType: itNRGBA, 47 imgBounds: img.Bounds(), 48 imgNRGBA: img, 49 } 50 51 case *image.NRGBA64: 52 p = &pixelGetter{ 53 imgType: itNRGBA64, 54 imgBounds: img.Bounds(), 55 imgNRGBA64: img, 56 } 57 58 case *image.RGBA: 59 p = &pixelGetter{ 60 imgType: itRGBA, 61 imgBounds: img.Bounds(), 62 imgRGBA: img, 63 } 64 65 case *image.RGBA64: 66 p = &pixelGetter{ 67 imgType: itRGBA64, 68 imgBounds: img.Bounds(), 69 imgRGBA64: img, 70 } 71 72 case *image.Gray: 73 p = &pixelGetter{ 74 imgType: itGray, 75 imgBounds: img.Bounds(), 76 imgGray: img, 77 } 78 79 case *image.Gray16: 80 p = &pixelGetter{ 81 imgType: itGray16, 82 imgBounds: img.Bounds(), 83 imgGray16: img, 84 } 85 86 case *image.YCbCr: 87 p = &pixelGetter{ 88 imgType: itYCbCr, 89 imgBounds: img.Bounds(), 90 imgYCbCr: img, 91 } 92 93 case *image.Paletted: 94 p = &pixelGetter{ 95 imgType: itPaletted, 96 imgBounds: img.Bounds(), 97 imgPaletted: img, 98 imgPalette: convertPalette(img.Palette), 99 } 100 return 101 102 default: 103 p = &pixelGetter{ 104 imgType: itGeneric, 105 imgBounds: img.Bounds(), 106 imgGeneric: img, 107 } 108 } 109 return 110 } 111 112 const ( 113 qf8 = float32(1.0 / 255.0) 114 qf16 = float32(1.0 / 65535.0) 115 epal = qf16 * qf16 / 2.0 116 ) 117 118 func convertPalette(p []color.Color) []pixel { 119 plen := len(p) 120 pnew := make([]pixel, plen) 121 for i := 0; i < plen; i++ { 122 r16, g16, b16, a16 := p[i].RGBA() 123 switch a16 { 124 case 0: 125 pnew[i] = pixel{0.0, 0.0, 0.0, 0.0} 126 case 65535: 127 r := float32(r16) * qf16 128 g := float32(g16) * qf16 129 b := float32(b16) * qf16 130 pnew[i] = pixel{r, g, b, 1.0} 131 default: 132 q := float32(1.0) / float32(a16) 133 r := float32(r16) * q 134 g := float32(g16) * q 135 b := float32(b16) * q 136 a := float32(a16) * qf16 137 pnew[i] = pixel{r, g, b, a} 138 } 139 } 140 return pnew 141 } 142 143 func getPaletteIndex(pal []pixel, px pixel) int { 144 var k int = 0 145 var dmin float32 = 4 146 for i, palpx := range pal { 147 d := px.R - palpx.R 148 dcur := d * d 149 d = px.G - palpx.G 150 dcur += d * d 151 d = px.B - palpx.B 152 dcur += d * d 153 d = px.A - palpx.A 154 dcur += d * d 155 if dcur < epal { 156 return i 157 } 158 if dcur < dmin { 159 dmin = dcur 160 k = i 161 } 162 } 163 return k 164 } 165 166 func pixelclr(c color.Color) (px pixel) { 167 r16, g16, b16, a16 := c.RGBA() 168 switch a16 { 169 case 0: 170 px = pixel{0.0, 0.0, 0.0, 0.0} 171 case 65535: 172 r := float32(r16) * qf16 173 g := float32(g16) * qf16 174 b := float32(b16) * qf16 175 px = pixel{r, g, b, 1.0} 176 default: 177 q := float32(1.0) / float32(a16) 178 r := float32(r16) * q 179 g := float32(g16) * q 180 b := float32(b16) * q 181 a := float32(a16) * qf16 182 px = pixel{r, g, b, a} 183 } 184 return px 185 } 186 187 func (p *pixelGetter) getPixel(x, y int) (px pixel) { 188 switch p.imgType { 189 case itNRGBA: 190 i := p.imgNRGBA.PixOffset(x, y) 191 r := float32(p.imgNRGBA.Pix[i+0]) * qf8 192 g := float32(p.imgNRGBA.Pix[i+1]) * qf8 193 b := float32(p.imgNRGBA.Pix[i+2]) * qf8 194 a := float32(p.imgNRGBA.Pix[i+3]) * qf8 195 px = pixel{r, g, b, a} 196 197 case itNRGBA64: 198 i := p.imgNRGBA64.PixOffset(x, y) 199 r := float32(uint16(p.imgNRGBA64.Pix[i+0])<<8|uint16(p.imgNRGBA64.Pix[i+1])) * qf16 200 g := float32(uint16(p.imgNRGBA64.Pix[i+2])<<8|uint16(p.imgNRGBA64.Pix[i+3])) * qf16 201 b := float32(uint16(p.imgNRGBA64.Pix[i+4])<<8|uint16(p.imgNRGBA64.Pix[i+5])) * qf16 202 a := float32(uint16(p.imgNRGBA64.Pix[i+6])<<8|uint16(p.imgNRGBA64.Pix[i+7])) * qf16 203 px = pixel{r, g, b, a} 204 205 case itRGBA: 206 i := p.imgRGBA.PixOffset(x, y) 207 a8 := p.imgRGBA.Pix[i+3] 208 switch a8 { 209 case 0: 210 px = pixel{0.0, 0.0, 0.0, 0.0} 211 case 255: 212 r := float32(p.imgRGBA.Pix[i+0]) * qf8 213 g := float32(p.imgRGBA.Pix[i+1]) * qf8 214 b := float32(p.imgRGBA.Pix[i+2]) * qf8 215 px = pixel{r, g, b, 1.0} 216 default: 217 q := float32(1.0) / float32(a8) 218 r := float32(p.imgRGBA.Pix[i+0]) * q 219 g := float32(p.imgRGBA.Pix[i+1]) * q 220 b := float32(p.imgRGBA.Pix[i+2]) * q 221 a := float32(a8) * qf8 222 px = pixel{r, g, b, a} 223 } 224 225 case itRGBA64: 226 i := p.imgRGBA64.PixOffset(x, y) 227 a16 := uint16(p.imgRGBA64.Pix[i+6])<<8 | uint16(p.imgRGBA64.Pix[i+7]) 228 switch a16 { 229 case 0: 230 px = pixel{0.0, 0.0, 0.0, 0.0} 231 case 65535: 232 r := float32(uint16(p.imgRGBA64.Pix[i+0])<<8|uint16(p.imgRGBA64.Pix[i+1])) * qf16 233 g := float32(uint16(p.imgRGBA64.Pix[i+2])<<8|uint16(p.imgRGBA64.Pix[i+3])) * qf16 234 b := float32(uint16(p.imgRGBA64.Pix[i+4])<<8|uint16(p.imgRGBA64.Pix[i+5])) * qf16 235 px = pixel{r, g, b, 1.0} 236 default: 237 q := float32(1.0) / float32(a16) 238 r := float32(uint16(p.imgRGBA64.Pix[i+0])<<8|uint16(p.imgRGBA64.Pix[i+1])) * q 239 g := float32(uint16(p.imgRGBA64.Pix[i+2])<<8|uint16(p.imgRGBA64.Pix[i+3])) * q 240 b := float32(uint16(p.imgRGBA64.Pix[i+4])<<8|uint16(p.imgRGBA64.Pix[i+5])) * q 241 a := float32(a16) * qf16 242 px = pixel{r, g, b, a} 243 } 244 245 case itGray: 246 i := p.imgGray.PixOffset(x, y) 247 v := float32(p.imgGray.Pix[i]) * qf8 248 px = pixel{v, v, v, 1.0} 249 250 case itGray16: 251 i := p.imgGray16.PixOffset(x, y) 252 v := float32(uint16(p.imgGray16.Pix[i+0])<<8|uint16(p.imgGray16.Pix[i+1])) * qf16 253 px = pixel{v, v, v, 1.0} 254 255 case itYCbCr: 256 iy := p.imgYCbCr.YOffset(x, y) 257 ic := p.imgYCbCr.COffset(x, y) 258 r8, g8, b8 := color.YCbCrToRGB(p.imgYCbCr.Y[iy], p.imgYCbCr.Cb[ic], p.imgYCbCr.Cr[ic]) 259 r := float32(r8) * qf8 260 g := float32(g8) * qf8 261 b := float32(b8) * qf8 262 px = pixel{r, g, b, 1.0} 263 264 case itPaletted: 265 i := p.imgPaletted.PixOffset(x, y) 266 k := p.imgPaletted.Pix[i] 267 px = p.imgPalette[k] 268 269 case itGeneric: 270 px = pixelclr(p.imgGeneric.At(x, y)) 271 } 272 return 273 } 274 275 func (p *pixelGetter) getPixelRow(y int, buf *[]pixel) { 276 *buf = (*buf)[0:0] 277 for x := p.imgBounds.Min.X; x != p.imgBounds.Max.X; x++ { 278 *buf = append(*buf, p.getPixel(x, y)) 279 } 280 } 281 282 func (p *pixelGetter) getPixelColumn(x int, buf *[]pixel) { 283 *buf = (*buf)[0:0] 284 for y := p.imgBounds.Min.Y; y != p.imgBounds.Max.Y; y++ { 285 *buf = append(*buf, p.getPixel(x, y)) 286 } 287 } 288 289 func f32u8(val float32) uint8 { 290 if val > 255.0 { 291 val = 255.0 292 } else if val < 0.0 { 293 val = 0.0 294 } 295 return uint8(val + 0.5) 296 } 297 298 func f32u16(val float32) uint16 { 299 if val > 65535.0 { 300 val = 65535.0 301 } else if val < 0.0 { 302 val = 0.0 303 } 304 return uint16(val + 0.5) 305 } 306 307 type pixelSetter struct { 308 imgType imageType 309 imgBounds image.Rectangle 310 imgGeneric draw.Image 311 imgNRGBA *image.NRGBA 312 imgNRGBA64 *image.NRGBA64 313 imgRGBA *image.RGBA 314 imgRGBA64 *image.RGBA64 315 imgGray *image.Gray 316 imgGray16 *image.Gray16 317 imgPaletted *image.Paletted 318 imgPalette []pixel 319 } 320 321 func newPixelSetter(img draw.Image) (p *pixelSetter) { 322 switch img := img.(type) { 323 case *image.NRGBA: 324 p = &pixelSetter{ 325 imgType: itNRGBA, 326 imgBounds: img.Bounds(), 327 imgNRGBA: img, 328 } 329 330 case *image.NRGBA64: 331 p = &pixelSetter{ 332 imgType: itNRGBA64, 333 imgBounds: img.Bounds(), 334 imgNRGBA64: img, 335 } 336 337 case *image.RGBA: 338 p = &pixelSetter{ 339 imgType: itRGBA, 340 imgBounds: img.Bounds(), 341 imgRGBA: img, 342 } 343 344 case *image.RGBA64: 345 p = &pixelSetter{ 346 imgType: itRGBA64, 347 imgBounds: img.Bounds(), 348 imgRGBA64: img, 349 } 350 351 case *image.Gray: 352 p = &pixelSetter{ 353 imgType: itGray, 354 imgBounds: img.Bounds(), 355 imgGray: img, 356 } 357 358 case *image.Gray16: 359 p = &pixelSetter{ 360 imgType: itGray16, 361 imgBounds: img.Bounds(), 362 imgGray16: img, 363 } 364 365 case *image.Paletted: 366 p = &pixelSetter{ 367 imgType: itPaletted, 368 imgBounds: img.Bounds(), 369 imgPaletted: img, 370 imgPalette: convertPalette(img.Palette), 371 } 372 373 default: 374 p = &pixelSetter{ 375 imgType: itGeneric, 376 imgBounds: img.Bounds(), 377 imgGeneric: img, 378 } 379 } 380 return 381 } 382 383 func (p *pixelSetter) setPixel(x, y int, px pixel) { 384 if !image.Pt(x, y).In(p.imgBounds) { 385 return 386 } 387 switch p.imgType { 388 case itNRGBA: 389 i := p.imgNRGBA.PixOffset(x, y) 390 p.imgNRGBA.Pix[i+0] = f32u8(px.R * 255.0) 391 p.imgNRGBA.Pix[i+1] = f32u8(px.G * 255.0) 392 p.imgNRGBA.Pix[i+2] = f32u8(px.B * 255.0) 393 p.imgNRGBA.Pix[i+3] = f32u8(px.A * 255.0) 394 395 case itNRGBA64: 396 r16 := f32u16(px.R * 65535.0) 397 g16 := f32u16(px.G * 65535.0) 398 b16 := f32u16(px.B * 65535.0) 399 a16 := f32u16(px.A * 65535.0) 400 i := p.imgNRGBA64.PixOffset(x, y) 401 p.imgNRGBA64.Pix[i+0] = uint8(r16 >> 8) 402 p.imgNRGBA64.Pix[i+1] = uint8(r16 & 0xff) 403 p.imgNRGBA64.Pix[i+2] = uint8(g16 >> 8) 404 p.imgNRGBA64.Pix[i+3] = uint8(g16 & 0xff) 405 p.imgNRGBA64.Pix[i+4] = uint8(b16 >> 8) 406 p.imgNRGBA64.Pix[i+5] = uint8(b16 & 0xff) 407 p.imgNRGBA64.Pix[i+6] = uint8(a16 >> 8) 408 p.imgNRGBA64.Pix[i+7] = uint8(a16 & 0xff) 409 410 case itRGBA: 411 fa := px.A * 255.0 412 i := p.imgRGBA.PixOffset(x, y) 413 p.imgRGBA.Pix[i+0] = f32u8(px.R * fa) 414 p.imgRGBA.Pix[i+1] = f32u8(px.G * fa) 415 p.imgRGBA.Pix[i+2] = f32u8(px.B * fa) 416 p.imgRGBA.Pix[i+3] = f32u8(fa) 417 418 case itRGBA64: 419 fa := px.A * 65535.0 420 r16 := f32u16(px.R * fa) 421 g16 := f32u16(px.G * fa) 422 b16 := f32u16(px.B * fa) 423 a16 := f32u16(fa) 424 i := p.imgRGBA64.PixOffset(x, y) 425 p.imgRGBA64.Pix[i+0] = uint8(r16 >> 8) 426 p.imgRGBA64.Pix[i+1] = uint8(r16 & 0xff) 427 p.imgRGBA64.Pix[i+2] = uint8(g16 >> 8) 428 p.imgRGBA64.Pix[i+3] = uint8(g16 & 0xff) 429 p.imgRGBA64.Pix[i+4] = uint8(b16 >> 8) 430 p.imgRGBA64.Pix[i+5] = uint8(b16 & 0xff) 431 p.imgRGBA64.Pix[i+6] = uint8(a16 >> 8) 432 p.imgRGBA64.Pix[i+7] = uint8(a16 & 0xff) 433 434 case itGray: 435 i := p.imgGray.PixOffset(x, y) 436 p.imgGray.Pix[i] = f32u8((0.299*px.R + 0.587*px.G + 0.114*px.B) * px.A * 255.0) 437 438 case itGray16: 439 i := p.imgGray16.PixOffset(x, y) 440 y16 := f32u16((0.299*px.R + 0.587*px.G + 0.114*px.B) * px.A * 65535.0) 441 p.imgGray16.Pix[i+0] = uint8(y16 >> 8) 442 p.imgGray16.Pix[i+1] = uint8(y16 & 0xff) 443 444 case itPaletted: 445 px1 := pixel{ 446 minf32(maxf32(px.R, 0), 1), 447 minf32(maxf32(px.G, 0), 1), 448 minf32(maxf32(px.B, 0), 1), 449 minf32(maxf32(px.A, 0), 1), 450 } 451 i := p.imgPaletted.PixOffset(x, y) 452 k := getPaletteIndex(p.imgPalette, px1) 453 p.imgPaletted.Pix[i] = uint8(k) 454 455 case itGeneric: 456 r16 := f32u16(px.R * 65535.0) 457 g16 := f32u16(px.G * 65535.0) 458 b16 := f32u16(px.B * 65535.0) 459 a16 := f32u16(px.A * 65535.0) 460 p.imgGeneric.Set(x, y, color.NRGBA64{r16, g16, b16, a16}) 461 } 462 } 463 464 func (p *pixelSetter) setPixelRow(y int, buf []pixel) { 465 for i, x := 0, p.imgBounds.Min.X; i < len(buf); i, x = i+1, x+1 { 466 p.setPixel(x, y, buf[i]) 467 } 468 } 469 470 func (p *pixelSetter) setPixelColumn(x int, buf []pixel) { 471 for i, y := 0, p.imgBounds.Min.Y; i < len(buf); i, y = i+1, y+1 { 472 p.setPixel(x, y, buf[i]) 473 } 474 }