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)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -85,7 +86,7 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 |
Loading…
Reference in a new issue