Parcourir la source

Merge branch 'alpha_channel_fix'

Colors in image.RGBA, image.RGBA64 and generic types with alpha channel should
now resize correctly. There's a small performance hit for these image types even
if they don't use alpha channels though.
Fixes bug #26.
nfnt il y a 10 ans
Parent
commit
f2d1b73023
2 fichiers modifiés avec 66 ajouts et 8 suppressions
  1. 51 8
      converter.go
  2. 15 0
      resize_test.go

+ 51 - 8
converter.go

@@ -64,6 +64,17 @@ func resizeGeneric(in image.Image, out *image.NRGBA64, scale float64, coeffs []i
 						xi = maxX
 					}
 					r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
+
+					// reverse alpha-premultiplication.
+					if a != 0 {
+						r *= 0xffff
+						r /= a
+						g *= 0xffff
+						g /= a
+						b *= 0xffff
+						b /= a
+					}
+
 					rgba[0] += int64(coeff) * int64(r)
 					rgba[1] += int64(coeff) * int64(g)
 					rgba[2] += int64(coeff) * int64(b)
@@ -112,10 +123,26 @@ func resizeRGBA(in *image.RGBA, out *image.NRGBA, scale float64, coeffs []int16,
 					default:
 						xi = 0
 					}
-					rgba[0] += int32(coeff) * int32(row[xi+0])
-					rgba[1] += int32(coeff) * int32(row[xi+1])
-					rgba[2] += int32(coeff) * int32(row[xi+2])
-					rgba[3] += int32(coeff) * int32(row[xi+3])
+
+					r := uint32(row[xi+0])
+					g := uint32(row[xi+1])
+					b := uint32(row[xi+2])
+					a := uint32(row[xi+3])
+
+					// reverse alpha-premultiplication.
+					if a != 0 {
+						r *= 0xffff
+						r /= a
+						g *= 0xffff
+						g /= a
+						b *= 0xffff
+						b /= a
+					}
+
+					rgba[0] += int32(coeff) * int32(r)
+					rgba[1] += int32(coeff) * int32(g)
+					rgba[2] += int32(coeff) * int32(b)
+					rgba[3] += int32(coeff) * int32(a)
 					sum += int32(coeff)
 				}
 			}
@@ -192,10 +219,26 @@ func resizeRGBA64(in *image.RGBA64, out *image.NRGBA64, scale float64, coeffs []
 					default:
 						xi = 0
 					}
-					rgba[0] += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
-					rgba[1] += int64(coeff) * int64(uint16(row[xi+2])<<8|uint16(row[xi+3]))
-					rgba[2] += int64(coeff) * int64(uint16(row[xi+4])<<8|uint16(row[xi+5]))
-					rgba[3] += int64(coeff) * int64(uint16(row[xi+6])<<8|uint16(row[xi+7]))
+
+					r := uint32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
+					g := uint32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
+					b := uint32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
+					a := uint32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
+
+					// reverse alpha-premultiplication.
+					if a != 0 {
+						r *= 0xffff
+						r /= a
+						g *= 0xffff
+						g /= a
+						b *= 0xffff
+						b /= a
+					}
+
+					rgba[0] += int64(coeff) * int64(r)
+					rgba[1] += int64(coeff) * int64(g)
+					rgba[2] += int64(coeff) * int64(b)
+					rgba[3] += int64(coeff) * int64(a)
 					sum += int64(coeff)
 				}
 			}

+ 15 - 0
resize_test.go

@@ -105,6 +105,21 @@ func Test_PixelCoordinates(t *testing.T) {
 	}
 }
 
+func Test_ResizeWithPremultipliedAlpha(t *testing.T) {
+	img := image.NewRGBA(image.Rect(0, 0, 1, 4))
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		// 0x80 = 0.5 * 0xFF.
+		img.SetRGBA(0, y, color.RGBA{0x80, 0x80, 0x80, 0x80})
+	}
+
+	out := Resize(1, 2, img, MitchellNetravali)
+
+	outputColor := out.At(0, 0).(color.NRGBA)
+	if outputColor.R != 0xFF {
+		t.Fail()
+	}
+}
+
 const (
 	// Use a small image size for benchmarks. We don't want memory performance
 	// to affect the benchmark results.