I need to decide if an image has alpha channel or not, so I write the code like this.
var HaveAlpha = func(Image image.Image) bool {
switch Image.ColorModel() {
case color.YCbCrModel, color.CMYKModel:
return false
case color.RGBAModel:
return !Image.(*image.RGBA).Opaque()
}
// ...
return false
}
So I need to list all the ColorModel
types and use Opaque()
to decide if the image has alpha channel or not (because I cannot use Opaque()
method in type image.Image
directly). And if an image has alpha channel but all pixels are opaque in the image (all RGBA of pixels in that image are like (*,*,*,255)
), this code may return wrong answer.
Is there a right or better way to decide if an image has alpha channel or not in Golang?
You may use type assertion to check if the concrete value stored in the image.Image
interface type has an Opaque() bool
method, and if so, simply call that and return its result. Note that all concrete image types in the image
package do have an Opaque()
method, so this will cover most cases.
If the image does not have such an Opaque()
method, loop over all pixels of the image and check if any of the pixel has an alpha value other than 0xff
, which means it's non-opaque.
Note that Image.At()
has a return type of the general color.Color
interface type, which only guarantees a single method: Color.RGBA()
. This RGBA()
method returns the alpha-premultiplied red, green, blue and alpha components, so if a pixel has 0xff
alpha value, that equals to 0xffff
when "alpha-premultiplied", so that's what we need to compare to.
func Opaque(im image.Image) bool {
// Check if image has Opaque() method:
if oim, ok := im.(interface {
Opaque() bool
}); ok {
return oim.Opaque() // It does, call it and return its result!
}
// No Opaque() method, we need to loop through all pixels and check manually:
rect := im.Bounds()
for y := rect.Min.Y; y < rect.Max.Y; y++ {
for x := rect.Min.X; x < rect.Max.X; x++ {
if _, _, _, a := im.At(x, y).RGBA(); a != 0xffff {
return false // Found a non-opaque pixel: image is non-opaque
}
}
}
return true // All pixels are opaque, so is the image
}
The above Opaque()
function will return true
if the image does not have an alpha channel, or it has but all pixels are opaque. It returns false
if and only if the image has alpha channel and there is at least 1 pixel that is not (fully) opaque.
Note: If an image does have an Opaque()
method, you can be sure that it takes existing pixels and their alpha values into consideration, so for example image.RGBA.Opaque()
also scans the entire image similarly to what we did above; but we had to do it in a general way, and Opaque()
implementations of concrete images may be much more efficient (so it is highly recommended to use the "shipped" Opaque()
method if it is available). As an example, implementation of image.YCbCr.Opaque()
is a simple return true
statement because YCbCr
images do not have alpha channel.