choose interpolation algorithm
This commit is contained in:
parent
c391300572
commit
71da9797fd
7 changed files with 105 additions and 53 deletions
|
@ -17,19 +17,19 @@ func saveToPngFile(filePath string, m image.Image) {
|
|||
f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
b := bufio.NewWriter(f)
|
||||
err = png.Encode(b, m)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
err = b.Flush()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
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() {
|
||||
|
||||
source := loadFromPngFile("../resource/image/Varna_Railway_Station_HDR.png")
|
||||
source := loadFromPngFile("../resource/image/TestAndroid.png")
|
||||
i := image.NewRGBA(1024, 768)
|
||||
gc := draw2d.NewImageGraphicContext(i)
|
||||
gc.Scale(2, 0.5)
|
||||
//gc.Translate(75, 25)
|
||||
// gc.Scale(2, 0.5)
|
||||
gc.Translate(float64(source.Bounds().Dx()/2), float64(source.Bounds().Dy()/2))
|
||||
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()
|
||||
gc.DrawImage(source)
|
||||
dt := time.Nanoseconds() - lastTime
|
||||
fmt.Printf("Draw image: %f ms\n", float64(dt)*1e-6)
|
||||
saveToPngFile("../resource/result/TestDrawImage.png", i)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -200,7 +200,7 @@ func (gc *ImageGraphicContext) Restore() {
|
|||
}
|
||||
|
||||
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() {
|
||||
|
|
|
@ -21,3 +21,31 @@ func squareDistance(x1, y1, x2, y2 float64) float64 {
|
|||
dy := y2 - y1
|
||||
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
|
||||
}
|
||||
|
|
|
@ -19,13 +19,6 @@ type NRGBAPainter struct {
|
|||
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.
|
||||
func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) {
|
||||
b := r.Image.Bounds()
|
||||
|
@ -57,9 +50,9 @@ func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) {
|
|||
a := M - (r.ca*ma)/M
|
||||
da = (da*a + r.ca*ma) / M
|
||||
if da != 0 {
|
||||
dr = min(M, (dr*a+r.cr*ma)/da)
|
||||
dg = min(M, (dg*a+r.cg*ma)/da)
|
||||
db = min(M, (db*a+r.cb*ma)/da)
|
||||
dr = minUint32(M, (dr*a+r.cr*ma)/da)
|
||||
dg = minUint32(M, (dg*a+r.cg*ma)/da)
|
||||
db = minUint32(M, (db*a+r.cb*ma)/da)
|
||||
} else {
|
||||
dr, dg, db = 0, 0, 0
|
||||
}
|
||||
|
@ -71,9 +64,9 @@ func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) {
|
|||
a := M - ma
|
||||
da = (da*a + r.ca*ma) / M
|
||||
if da != 0 {
|
||||
dr = min(M, (dr*a+r.cr*ma)/da)
|
||||
dg = min(M, (dg*a+r.cg*ma)/da)
|
||||
db = min(M, (db*a+r.cb*ma)/da)
|
||||
dr = minUint32(M, (dr*a+r.cr*ma)/da)
|
||||
dg = minUint32(M, (dg*a+r.cg*ma)/da)
|
||||
db = minUint32(M, (db*a+r.cb*ma)/da)
|
||||
} else {
|
||||
dr, dg, db = 0, 0, 0
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ import (
|
|||
"image"
|
||||
"math"
|
||||
)
|
||||
|
||||
type ImageFilter int
|
||||
|
||||
const (
|
||||
linearFilter ImageFilter = iota
|
||||
bilinearFilter
|
||||
bicubicFilter
|
||||
LinearFilter ImageFilter = iota
|
||||
BilinearFilter
|
||||
BicubicFilter
|
||||
)
|
||||
|
||||
//see http://pippin.gimp.org/image_processing/chap_resampling.html
|
||||
|
@ -59,7 +60,7 @@ func getColorCubicRow(img image.Image, x, y, offset float64) image.Color {
|
|||
r1, g1, b1, a1 := c1.RGBA()
|
||||
r2, g2, b2, a2 := c2.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)}
|
||||
}
|
||||
|
||||
|
@ -77,15 +78,15 @@ func getColorBicubic(img image.Image, x, y float64) image.Color {
|
|||
r1, g1, b1, a1 := c1.RGBA()
|
||||
r2, g2, b2, a2 := c2.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)}
|
||||
}
|
||||
|
||||
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
|
||||
return uint32((((( -7 * v0 + 21 * v1 - 21 * v2 + 7 * v3 ) * offset +
|
||||
( 15 * v0 - 36 * v1 + 27 * v2 - 6 * v3 ) ) * offset +
|
||||
( -9 * v0 + 9 * v2 ) ) * offset + (v0 + 16 * v1 + v2) ) / 18.0);
|
||||
return uint32(((((-7*v0+21*v1-21*v2+7*v3)*offset+
|
||||
(15*v0-36*v1+27*v2-6*v3))*offset+
|
||||
(-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0)
|
||||
}
|
||||
|
||||
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)}
|
||||
}
|
||||
|
||||
|
||||
func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) {
|
||||
//width := float64(src.Bounds().Dx())
|
||||
//height := float64(src.Bounds().Dy())
|
||||
//tr.InverseTransform(&width, &height)
|
||||
// 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())
|
||||
b := src.Bounds()
|
||||
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)
|
||||
var x, y, u, v float64
|
||||
for x = 0; x < width; x++ {
|
||||
for y = 0; y < height; y++ {
|
||||
for x = x0; x < x1; x++ {
|
||||
for y = y0; y < y1; y++ {
|
||||
u = x
|
||||
v = y
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
for _, point := range points {
|
||||
x := float64(point.X) / 256
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 346 KiB After Width: | Height: | Size: 23 KiB |
Loading…
Reference in a new issue