I am trying to convert WebP images to PNGs but I am having some difficulties with the WebP, Image, and Color packages.
The WebP decoding package returns an image.Image
, which uses alpha-premultiplied colors.
https://golang.org/pkg/image/color/#Color
type Color interface {
// An alpha-premultiplied color component c has been scaled by alpha (a),
// so has valid values 0 <= c <= a.
RGBA() (r, g, b, a uint32)
}
The problem is the WebP textures that I am trying to convert use the alpha channel as a specular map rather than for transparency. So even if the alpha on a particular pixel is 0, the color value is still important.
But with alpha-premultiplication, that pixel's color data would be irreversibly lost because anything * 0 = 0
.
Here is my code to convert WebP to PNG:
if extension == ".webp" {
//open the WebP file
webpFile, err := os.OpenFile(fp, os.O_RDWR, 0777)
defer webpFile.Close()
if err != nil {
return
}
//create reader
reader := bufio.NewReader(webpFile)
//decode the WebP into an image.Image
img, err := webp.Decode(reader)
if err != nil {
return
}
webpFile.Close()
//create the new PNG file
os.MkdirAll(destination + parentDirs, 0755)
pngFile, err := os.Create(destination + parentDirs + baseName + ".png")
defer pngFile.Close()
if err != nil {
return
}
//write to the PNG file
err = png.Encode(pngFile,img)
if err != nil {
return
}
}
What I want is to output a PNG that is fully opaque and retains all of the color data that was in the original WebP, even if a given pixel in the WebP was partially or fully transparent.
i.e. if I could just take the original texture and set every pixel's alpha value to 1 without disturbing the RGB values.
I have tried converting the color model for the img
, but I am still confused on how the model system works. I don't think this would solve the problem anyway because, as I understand, the conversion would still convert from the already alpha-premultiplied colors.
I have also tried undoing the alpha-premultplication for each pixel in img
by dividing each RGB value by the alpha. This works except for when the alpha was 0 because then all of the RGB data was lost when it was multiplied by 0.
So is there any way to convert from WebP to an image.Image
and then to a PNG without alpha-premultiplied colors?
Edit:
This was solved here: https://github.com/golang/go/issues/26001
I used separate functions to handle transparent WebPs.
func ConvertNYCbCrA(Color color.NYCbCrA) color.RGBA {
r,g,b := color.YCbCrToRGB(Color.Y,Color.Cb,Color.Cr)
return color.RGBA{r,g,b,255}
}
func ConvertNRGBA(Color color.NRGBA) color.RGBA {
return color.RGBA{
Color.R,
Color.G,
Color.B,
255,
}
}
/************************/
if extension == ".webp" {
//open the WebP file
webpFile, _ := os.OpenFile(filepath, os.O_RDWR, 0777)
defer webpFile.Close()
//create a reader
reader := bufio.NewReader(webpFile)
//decode the WebP
imageData, _ := webp.Decode(reader)
webpFile.Close()
//create new image
newImage := image.NewRGBA(imageData.Bounds())
//fill new image with pixels
for y := imageData.Bounds().Min.Y; y < imageData.Bounds().Max.Y; y++ {
for x := imageData.Bounds().Min.X; x < imageData.Bounds().Max.X; x++ {
//get pixel from imageData
pixel := imageData.At(x,y)
//convert pixel to RGBA
var RGBApixel color.RGBA
switch imageData.ColorModel() {
case color.NYCbCrAModel:
RGBApixel = ConvertNYCbCrA(pixel.(color.NYCbCrA))
case color.NRGBAModel:
RGBApixel = ConvertNRGBA(pixel.(color.NRGBA))
default:
RGBApixel = color.RGBAModel.Convert(pixel).(color.RGBA)
RGBApixel.A = 255
}
//set new pixel in new image
newImage.Set(x,y,RGBApixel)
}
}
//create the new PNG file
pngFile, _ := os.Create(newFilePath + ".png")
defer pngFile.Close()
//write to the PNG file
png.Encode(pngFile, newImage)
}