choose interpolation algorithm

This commit is contained in:
Laurent Le Goff 2011-04-14 12:17:11 +02:00
parent c391300572
commit 71da9797fd
7 changed files with 105 additions and 53 deletions

View file

@ -17,19 +17,19 @@ func saveToPngFile(filePath string, m image.Image) {
f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600) f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
os.Exit(1) return
} }
defer f.Close() defer f.Close()
b := bufio.NewWriter(f) b := bufio.NewWriter(f)
err = png.Encode(b, m) err = png.Encode(b, m)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
os.Exit(1) return
} }
err = b.Flush() err = b.Flush()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
os.Exit(1) return
} }
fmt.Printf("Wrote %s OK.\n", filePath) fmt.Printf("Wrote %s OK.\n", filePath)
} }
@ -52,29 +52,20 @@ func loadFromPngFile(filePath string) image.Image {
} }
func testBubble(gc draw2d.GraphicContext) {
gc.BeginPath()
gc.MoveTo(75, 25)
gc.QuadCurveTo(25, 25, 25, 62.5)
gc.QuadCurveTo(25, 100, 50, 100)
gc.QuadCurveTo(50, 120, 30, 125)
gc.QuadCurveTo(60, 120, 65, 100)
gc.QuadCurveTo(125, 100, 125, 62.5)
gc.QuadCurveTo(125, 25, 75, 25)
gc.Stroke()
}
func main() { func main() {
source := loadFromPngFile("../resource/image/TestAndroid.png")
source := loadFromPngFile("../resource/image/Varna_Railway_Station_HDR.png")
i := image.NewRGBA(1024, 768) i := image.NewRGBA(1024, 768)
gc := draw2d.NewImageGraphicContext(i) gc := draw2d.NewImageGraphicContext(i)
gc.Scale(2, 0.5) // gc.Scale(2, 0.5)
//gc.Translate(75, 25) gc.Translate(float64(source.Bounds().Dx()/2), float64(source.Bounds().Dy()/2))
gc.Rotate(30 * math.Pi / 180) gc.Rotate(30 * math.Pi / 180)
gc.Translate(float64(-source.Bounds().Dx()/2), float64(-source.Bounds().Dy()/2))
gc.Translate(75, 25)
lastTime := time.Nanoseconds() lastTime := time.Nanoseconds()
gc.DrawImage(source) gc.DrawImage(source)
dt := time.Nanoseconds() - lastTime dt := time.Nanoseconds() - lastTime
fmt.Printf("Draw image: %f ms\n", float64(dt)*1e-6) fmt.Printf("Draw image: %f ms\n", float64(dt)*1e-6)
saveToPngFile("../resource/result/TestDrawImage.png", i) saveToPngFile("../resource/result/TestDrawImage.png", i)
} }

View file

@ -200,7 +200,7 @@ func (gc *ImageGraphicContext) Restore() {
} }
func (gc *ImageGraphicContext) DrawImage(img image.Image) { func (gc *ImageGraphicContext) DrawImage(img image.Image) {
DrawImage(img, gc.img, gc.current.tr, draw.Over, linearFilter) DrawImage(img, gc.img, gc.current.tr, draw.Over, BilinearFilter)
} }
func (gc *ImageGraphicContext) BeginPath() { func (gc *ImageGraphicContext) BeginPath() {

View file

@ -21,3 +21,31 @@ func squareDistance(x1, y1, x2, y2 float64) float64 {
dy := y2 - y1 dy := y2 - y1
return dx*dx + dy*dy return dx*dx + dy*dy
} }
func min(x, y float64) float64 {
if x < y {
return x
}
return y
}
func max(x, y float64) float64 {
if x > y {
return x
}
return y
}
func minMax(x, y float64) (min, max float64) {
if x > y {
return y, x
}
return x, y
}
func minUint32(a, b uint32) uint32 {
if a < b {
return a
}
return b
}

View file

@ -19,13 +19,6 @@ type NRGBAPainter struct {
cr, cg, cb, ca uint32 cr, cg, cb, ca uint32
} }
func min(a, b uint32) uint32 {
if a < b {
return a
}
return b
}
// Paint satisfies the Painter interface by painting ss onto an image.RGBA. // Paint satisfies the Painter interface by painting ss onto an image.RGBA.
func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) { func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) {
b := r.Image.Bounds() b := r.Image.Bounds()
@ -57,9 +50,9 @@ func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) {
a := M - (r.ca*ma)/M a := M - (r.ca*ma)/M
da = (da*a + r.ca*ma) / M da = (da*a + r.ca*ma) / M
if da != 0 { if da != 0 {
dr = min(M, (dr*a+r.cr*ma)/da) dr = minUint32(M, (dr*a+r.cr*ma)/da)
dg = min(M, (dg*a+r.cg*ma)/da) dg = minUint32(M, (dg*a+r.cg*ma)/da)
db = min(M, (db*a+r.cb*ma)/da) db = minUint32(M, (db*a+r.cb*ma)/da)
} else { } else {
dr, dg, db = 0, 0, 0 dr, dg, db = 0, 0, 0
} }
@ -71,9 +64,9 @@ func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) {
a := M - ma a := M - ma
da = (da*a + r.ca*ma) / M da = (da*a + r.ca*ma) / M
if da != 0 { if da != 0 {
dr = min(M, (dr*a+r.cr*ma)/da) dr = minUint32(M, (dr*a+r.cr*ma)/da)
dg = min(M, (dg*a+r.cg*ma)/da) dg = minUint32(M, (dg*a+r.cg*ma)/da)
db = min(M, (db*a+r.cb*ma)/da) db = minUint32(M, (db*a+r.cb*ma)/da)
} else { } else {
dr, dg, db = 0, 0, 0 dr, dg, db = 0, 0, 0
} }

View file

@ -5,12 +5,13 @@ import (
"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
@ -59,8 +60,8 @@ func getColorCubicRow(img image.Image, x, y, offset float64) 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()
r3, g3, b3, a3 := c3.RGBA() r3, g3, b3, a3 := c3.RGBA()
r, g, b, a := cubic(offset,float64(r0),float64(r1),float64(r2),float64(r3)), cubic(offset,float64(g0),float64(g1),float64(g2),float64(g3)), cubic(offset,float64(b0),float64(b1),float64(b2),float64(b3)), cubic(offset,float64(a0),float64(a1),float64(a2),float64(a3)) r, g, b, a := cubic(offset, float64(r0), float64(r1), float64(r2), float64(r3)), cubic(offset, float64(g0), float64(g1), float64(g2), float64(g3)), cubic(offset, float64(b0), float64(b1), float64(b2), float64(b3)), cubic(offset, float64(a0), float64(a1), float64(a2), float64(a3))
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 getColorBicubic(img image.Image, x, y float64) image.Color { func getColorBicubic(img image.Image, x, y float64) image.Color {
@ -77,15 +78,15 @@ func getColorBicubic(img image.Image, x, y float64) 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()
r3, g3, b3, a3 := c3.RGBA() r3, g3, b3, a3 := c3.RGBA()
r, g, b, a := cubic(dy,float64(r0),float64(r1),float64(r2),float64(r3)), cubic(dy,float64(g0),float64(g1),float64(g2),float64(g3)), cubic(dy,float64(b0),float64(b1),float64(b2),float64(b3)), cubic(dy,float64(a0),float64(a1),float64(a2),float64(a3)) r, g, b, a := cubic(dy, float64(r0), float64(r1), float64(r2), float64(r3)), cubic(dy, float64(g0), float64(g1), float64(g2), float64(g3)), cubic(dy, float64(b0), float64(b1), float64(b2), float64(b3)), cubic(dy, float64(a0), float64(a1), float64(a2), float64(a3))
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 cubic(offset,v0,v1,v2,v3 float64) uint32{ 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 {
@ -99,20 +100,42 @@ func compose(c1, c2 image.Color) image.Color {
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) {
//width := float64(src.Bounds().Dx()) b := src.Bounds()
//height := float64(src.Bounds().Dy()) x0, y0, x1, y1 := float64(b.Min.X), float64(b.Min.Y), float64(b.Max.X), float64(b.Max.Y)
//tr.InverseTransform(&width, &height) tr.TransformRectangle(&x0, &y0, &x1, &y1)
// TODO: find a x0, y0, x1, y1 that fits into dest 0, width, 0, height is too large
width := float64(dest.Bounds().Dx())
height:= float64(dest.Bounds().Dy())
var x, y, u, v float64 var x, y, u, v float64
for x = 0; x < width; x++ { for x = x0; x < x1; x++ {
for y = 0; y < height; y++ { for y = y0; y < y1; y++ {
u = x u = x
v = y v = y
tr.InverseTransform(&u, &v) tr.InverseTransform(&u, &v)
dest.Set(int(x), int(y), compose(dest.At(int(x), int(y)), getColorLinear(src, u, v))) c1 := dest.At(int(x), int(y))
var c2 image.Color
switch filter {
case LinearFilter:
c2 = getColorLinear(src, u, v)
case BilinearFilter:
c2 = getColorBilinear(src, u, v)
case BicubicFilter:
c2 = getColorBicubic(src, u, v)
}
var cr image.Color
switch op {
case draw.Over:
r1, g1, b1, a1 := c1.RGBA()
r2, g2, b2, a2 := c2.RGBA()
ia := M - a2
r := ((r1 * ia) / M) + r2
g := ((g1 * ia) / M) + g2
b := ((b1 * ia) / M) + b2
a := ((a1 * ia) / M) + a2
cr = image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
default:
cr = c2
}
dest.Set(int(x), int(y), cr)
} }
} }
} }

View file

@ -26,6 +26,23 @@ func (tr MatrixTransform) Transform(points ...*float64) {
} }
} }
func (tr MatrixTransform) TransformRectangle(x0, y0, x2, y2*float64) {
x1 := *x2
y1 := *y0
x3 := *x0
y3 := *y2
tr.Transform(x0, y0, &x1, &y1, x2, y2, &x3, &y3)
*x0, x1 = minMax(*x0, x1)
*x2, x3 = minMax(*x2, x3)
*y0, y1 = minMax(*y0, y1)
*y2, y3 = minMax(*y2, y3)
*x0 = min(*x0, *x2)
*y0 = min(*y0, *y2)
*x2 = max(x1, x3)
*y2 = max(y1, y3)
}
func (tr MatrixTransform) TransformRasterPoint(points ...*raster.Point) { func (tr MatrixTransform) TransformRasterPoint(points ...*raster.Point) {
for _, point := range points { for _, point := range points {
x := float64(point.X) / 256 x := float64(point.X) / 256

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 KiB

After

Width:  |  Height:  |  Size: 23 KiB