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  }