package main import ( "math" "image" "image/color" ) // round rounds float number to it's nearest integer part. func round(x float64) float64 { t := math.Trunc(x) if math.Abs(x-t) >= 0.5 { return t + math.Copysign(1, x) } return t } // clamp255 converts a float64 number to uint8. func clamp255(x float64) uint8 { if x < 0 { return 0 } if x > 255 { return 255 } return uint8(x) } // max returns the biggest value between two numbers. func max(x, y int) float64 { if x > y { return float64(x) } return float64(y) } // unique returns slice's unique values. func unique(intSlice []int) []int { keys := make(map[int]bool) list := []int{} for _, entry := range intSlice { if _, value := keys[entry]; !value { keys[entry] = true list = append(list, entry) } } return list } // RGBtoYUV converts RGB to YUV color space. func RGBtoYUV(r, g, b uint32) (uint32, uint32, uint32) { y := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b) u := (((float64(b) - float64(y)) * 0.493) + 111) / 222 * 255 v := (((float64(r) - float64(y)) * 0.877) + 155) / 312 * 255 return uint32(y), uint32(u), uint32(v) } // YUVtoRGB converts YUV to RGB color space. func YUVtoRGB(y, u, v uint32) (uint32, uint32, uint32) { r := float64(y) + (1.13983 * float64(v)) g := float64(y) - (0.39465 * float64(u)) - (0.58060 * float64(v)) b := float64(y) + (2.03211 * float64(u)) return uint32(r), uint32(g), uint32(b) } // Converts any image type to *image.NRGBA with min-point at (0, 0). func imgToNRGBA(img image.Image) *image.NRGBA { srcBounds := img.Bounds() if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 { if src0, ok := img.(*image.NRGBA); ok { return src0 } } srcMinX := srcBounds.Min.X srcMinY := srcBounds.Min.Y dstBounds := srcBounds.Sub(srcBounds.Min) dstW := dstBounds.Dx() dstH := dstBounds.Dy() dst := image.NewNRGBA(dstBounds) switch src := img.(type) { case *image.NRGBA: rowSize := srcBounds.Dx() * 4 for dstY := 0; dstY < dstH; dstY++ { di := dst.PixOffset(0, dstY) si := src.PixOffset(srcMinX, srcMinY+dstY) for dstX := 0; dstX < dstW; dstX++ { copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize]) } } case *image.YCbCr: for dstY := 0; dstY < dstH; dstY++ { di := dst.PixOffset(0, dstY) for dstX := 0; dstX < dstW; dstX++ { srcX := srcMinX + dstX srcY := srcMinY + dstY siy := src.YOffset(srcX, srcY) sic := src.COffset(srcX, srcY) r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic]) dst.Pix[di+0] = r dst.Pix[di+1] = g dst.Pix[di+2] = b dst.Pix[di+3] = 0xff di += 4 } } default: for dstY := 0; dstY < dstH; dstY++ { di := dst.PixOffset(0, dstY) for dstX := 0; dstX < dstW; dstX++ { c := color.NRGBAModel.Convert(img.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA) dst.Pix[di+0] = c.R dst.Pix[di+1] = c.G dst.Pix[di+2] = c.B dst.Pix[di+3] = c.A di += 4 } } } return dst }