enhance bilinear algo

This commit is contained in:
Laurent Le Goff 2011-04-14 15:42:13 +02:00
parent 45159ffd27
commit 02b785a4df
3 changed files with 146 additions and 140 deletions

View file

@ -60,8 +60,9 @@ func main() {
tr := draw2d.NewIdentityMatrix() tr := draw2d.NewIdentityMatrix()
tr.Translate(width/2, height/2) tr.Translate(width/2, height/2)
tr.Rotate(30 * math.Pi / 180) tr.Rotate(30 * math.Pi / 180)
//tr.Scale(3, 3)
tr.Translate(-width/2, -height/2) tr.Translate(-width/2, -height/2)
tr.Translate(75, 25) tr.Translate(200, 5)
lastTime := time.Nanoseconds() lastTime := time.Nanoseconds()
draw2d.DrawImage(source, dest, tr, draw.Over, draw2d.BilinearFilter) draw2d.DrawImage(source, dest, tr, draw.Over, draw2d.BilinearFilter)
dt := time.Nanoseconds() - lastTime dt := time.Nanoseconds() - lastTime

View file

@ -1,37 +1,48 @@
package draw2d package draw2d
import ( import (
"exp/draw" "exp/draw"
"image" "image"
"math" "math"
) )
type ImageFilter int type ImageFilter int
const ( const (
LinearFilter ImageFilter = iota LinearFilter ImageFilter = iota
BilinearFilter BilinearFilter
BicubicFilter BicubicFilter
) )
//see http://pippin.gimp.org/image_processing/chap_resampling.html //see http://pippin.gimp.org/image_processing/chap_resampling.html
func getColorLinear(img image.Image, x, y float64) image.Color { func getColorLinear(img image.Image, x, y float64) image.Color {
return img.At(int(x), int(y)) return img.At(int(x), int(y))
} }
func getColorBilinear(img image.Image, x, y float64) image.Color { func getColorBilinear(img image.Image, x, y float64) image.Color {
x0 := math.Floor(x) x0 := math.Floor(x)
y0 := math.Floor(y) y0 := math.Floor(y)
dx := x - x0 dx := x - x0
dy := y - y0 dy := y - y0
color0 := img.At(int(x0), int(y0)) c0 := img.At(int(x0), int(y0))
color1 := img.At(int(x0+1), int(y0)) c1 := img.At(int(x0+1), int(y0))
color2 := img.At(int(x0+1), int(y0+1)) c2 := img.At(int(x0+1), int(y0+1))
color3 := img.At(int(x0), int(y0+1)) c3 := img.At(int(x0), int(y0+1))
rt, gt, bt, at := c0.RGBA()
return lerp(lerp(color0, color1, dx), lerp(color3, color2, dx), dy) r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
} rt, gt, bt, at = c1.RGBA()
r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c2.RGBA()
r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c3.RGBA()
r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
r := int(lerp(lerp(r0, r1, dx), lerp(r3, r2, dx), dy))
g := int(lerp(lerp(g0, g1, dx), lerp(g3, g2, dx), dy))
b := int(lerp(lerp(b0, b1, dx), lerp(b3, b2, dx), dy))
a := int(lerp(lerp(a0, a1, dx), lerp(a3, a2, dx), dy))
return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
}
/** /**
-- LERP -- LERP
-- /lerp/, vi.,n. -- /lerp/, vi.,n.
@ -39,110 +50,104 @@ func getColorBilinear(img image.Image, x, y float64) image.Color {
-- Quasi-acronym for Linear Interpolation, used as a verb or noun for -- Quasi-acronym for Linear Interpolation, used as a verb or noun for
-- the operation. "Bresenham's algorithm lerps incrementally between the -- the operation. "Bresenham's algorithm lerps incrementally between the
-- two endpoints of the line." (From Jargon File (4.4.4, 14 Aug 2003) -- two endpoints of the line." (From Jargon File (4.4.4, 14 Aug 2003)
*/ */
func lerp(c1, c2 image.Color, ratio float64) image.Color { func lerp(v1, v2, ratio float64) float64 {
r1, g1, b1, a1 := c1.RGBA() return v1*(1-ratio) + v2*ratio
r2, g2, b2, a2 := c2.RGBA() }
r := int(float64(r1)*(1-ratio) + float64(r2)*ratio)
g := int(float64(g1)*(1-ratio) + float64(g2)*ratio)
b := int(float64(b1)*(1-ratio) + float64(b2)*ratio) func getColorCubicRow(img image.Image, x, y, offset float64) image.Color {
a := int(float64(a1)*(1-ratio) + float64(a2)*ratio) c0 := img.At(int(x), int(y))
return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} c1 := img.At(int(x+1), int(y))
} c2 := img.At(int(x+2), int(y))
c3 := img.At(int(x+3), int(y))
rt, gt, bt, at := c0.RGBA()
func getColorCubicRow(img image.Image, x, y, offset float64) image.Color { r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
c0 := img.At(int(x), int(y)) rt, gt, bt, at = c1.RGBA()
c1 := img.At(int(x+1), int(y)) r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
c2 := img.At(int(x+2), int(y)) rt, gt, bt, at = c2.RGBA()
c3 := img.At(int(x+3), int(y)) r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at := c0.RGBA() rt, gt, bt, at = c3.RGBA()
r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c1.RGBA() r, g, b, a := cubic(offset, r0, r1, r2, r3), cubic(offset, g0, g1, g2, g3), cubic(offset, b0, b1, b2, b3), cubic(offset, a0, a1, a2, a3)
r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
rt, gt, bt, at = c2.RGBA() }
r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c3.RGBA() func getColorBicubic(img image.Image, x, y float64) image.Color {
r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) x0 := math.Floor(x)
r, g, b, a := cubic(offset, r0, r1, r2, r3), cubic(offset, g0, g1, g2, g3), cubic(offset, b0, b1, b2, b3), cubic(offset, a0, a1, a2, a3) y0 := math.Floor(y)
return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} dx := x - x0
} dy := y - y0
c0 := getColorCubicRow(img, x0-1, y0-1, dx)
func getColorBicubic(img image.Image, x, y float64) image.Color { c1 := getColorCubicRow(img, x0-1, y0, dx)
x0 := math.Floor(x) c2 := getColorCubicRow(img, x0-1, y0+1, dx)
y0 := math.Floor(y) c3 := getColorCubicRow(img, x0-1, y0+2, dx)
dx := x - x0 rt, gt, bt, at := c0.RGBA()
dy := y - y0 r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
c0 := getColorCubicRow(img, x0-1, y0-1, dx) rt, gt, bt, at = c1.RGBA()
c1 := getColorCubicRow(img, x0-1, y0, dx) r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
c2 := getColorCubicRow(img, x0-1, y0+1, dx) rt, gt, bt, at = c2.RGBA()
c3 := getColorCubicRow(img, x0-1, y0+2, dx) r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at := c0.RGBA() rt, gt, bt, at = c3.RGBA()
r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c1.RGBA() r, g, b, a := cubic(dy, r0, r1, r2, r3), cubic(dy, g0, g1, g2, g3), cubic(dy, b0, b1, b2, b3), cubic(dy, a0, a1, a2, a3)
r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
rt, gt, bt, at = c2.RGBA() }
r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c3.RGBA() func cubic(offset, v0, v1, v2, v3 float64) uint32 {
r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
r, g, b, a := cubic(dy, r0, r1, r2, r3), cubic(dy, g0, g1, g2, g3), cubic(dy, b0, b1, b2, b3), cubic(dy, a0, a1, a2, a3)
return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
}
func cubic(offset, v0, v1, v2, v3 float64) uint32 {
// offset is the offset of the sampled value between v1 and v2 // offset is the offset of the sampled value between v1 and v2
return uint32(((((-7*v0+21*v1-21*v2+7*v3)*offset+ return uint32(((((-7*v0+21*v1-21*v2+7*v3)*offset+
(15*v0-36*v1+27*v2-6*v3))*offset+ (15*v0-36*v1+27*v2-6*v3))*offset+
(-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0) (-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0)
} }
func compose(c1, c2 image.Color) image.Color { func compose(c1, c2 image.Color) image.Color {
r1, g1, b1, a1 := c1.RGBA() r1, g1, b1, a1 := c1.RGBA()
r2, g2, b2, a2 := c2.RGBA() r2, g2, b2, a2 := c2.RGBA()
ia := M - a2 ia := M - a2
r := ((r1 * ia) / M) + r2 r := ((r1 * ia) / M) + r2
g := ((g1 * ia) / M) + g2 g := ((g1 * ia) / M) + g2
b := ((b1 * ia) / M) + b2 b := ((b1 * ia) / M) + b2
a := ((a1 * ia) / M) + a2 a := ((a1 * ia) / M) + a2
return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
} }
func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) { func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) {
b := src.Bounds() b := src.Bounds()
x0, y0, x1, y1 := float64(b.Min.X), float64(b.Min.Y), float64(b.Max.X), float64(b.Max.Y) x0, y0, x1, y1 := float64(b.Min.X), float64(b.Min.Y), float64(b.Max.X), float64(b.Max.Y)
tr.TransformRectangle(&x0, &y0, &x1, &y1) tr.TransformRectangle(&x0, &y0, &x1, &y1)
var x, y, u, v float64 var x, y, u, v float64
for x = x0; x < x1; x++ { for x = x0; x < x1; x++ {
for y = y0; y < y1; y++ { for y = y0; y < y1; y++ {
u = x u = x
v = y v = y
tr.InverseTransform(&u, &v) tr.InverseTransform(&u, &v)
c1 := dest.At(int(x), int(y)) c1 := dest.At(int(x), int(y))
var c2 image.Color var c2 image.Color
switch filter { switch filter {
case LinearFilter: case LinearFilter:
c2 = src.At(int(u), int(v)) c2 = src.At(int(u), int(v))
case BilinearFilter: case BilinearFilter:
c2 = getColorBilinear(src, u, v) c2 = getColorBilinear(src, u, v)
case BicubicFilter: case BicubicFilter:
c2 = getColorBicubic(src, u, v) c2 = getColorBicubic(src, u, v)
} }
var cr image.Color var cr image.Color
switch op { switch op {
case draw.Over: case draw.Over:
r1, g1, b1, a1 := c1.RGBA() r1, g1, b1, a1 := c1.RGBA()
r2, g2, b2, a2 := c2.RGBA() r2, g2, b2, a2 := c2.RGBA()
ia := M - a2 ia := M - a2
r := ((r1 * ia) / M) + r2 r := ((r1 * ia) / M) + r2
g := ((g1 * ia) / M) + g2 g := ((g1 * ia) / M) + g2
b := ((b1 * ia) / M) + b2 b := ((b1 * ia) / M) + b2
a := ((a1 * ia) / M) + a2 a := ((a1 * ia) / M) + a2
cr = image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} cr = image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
default: default:
cr = c2 cr = c2
} }
dest.Set(int(x), int(y), cr) dest.Set(int(x), int(y), cr)
} }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB