diff --git a/draw2d/Makefile b/draw2d/Makefile index 32d152f..2cecc03 100644 --- a/draw2d/Makefile +++ b/draw2d/Makefile @@ -19,5 +19,6 @@ GOFILES=\ vertex2d.go\ gc.go\ paint.go\ + rgba_interpolation.go\ include $(GOROOT)/src/Make.pkg diff --git a/draw2d/draw2d.go b/draw2d/draw2d.go index d1eed46..a73bf4f 100644 --- a/draw2d/draw2d.go +++ b/draw2d/draw2d.go @@ -6,7 +6,6 @@ import ( "exp/draw" "image" "log" - "math" "freetype-go.googlecode.com/hg/freetype" "freetype-go.googlecode.com/hg/freetype/raster" ) @@ -199,105 +198,9 @@ func (gc *ImageGraphicContext) Restore() { oldContext.previous = nil } } -//see http://pippin.gimp.org/image_processing/chap_resampling.html -func getColorLinear(img image.Image, x, y float64) image.Color { - return img.At(int(x), int(y)) -} - -func getColorBilinear(img image.Image, x, y float64) image.Color { - x0 := math.Floor(x) - y0 := math.Floor(y) - dx := x - x0 - dy := y - y0 - - color0 := img.At(int(x0), int(y0)) - color1 := img.At(int(x0+1), int(y0)) - color2 := img.At(int(x0+1), int(y0+1)) - color3 := img.At(int(x0), int(y0+1)) - - return lerp(lerp(color0, color1, dx), lerp(color3, color2, dx), dy) -} -/** --- LERP --- /lerp/, vi.,n. --- --- Quasi-acronym for Linear Interpolation, used as a verb or noun for --- the operation. "Bresenham's algorithm lerps incrementally between the --- two endpoints of the line." (From Jargon File (4.4.4, 14 Aug 2003) -*/ -func lerp(c1, c2 image.Color, ratio float64) image.Color { - r1, g1, b1, a1 := c1.RGBA() - 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) - a := int(float64(a1)*(1-ratio) + float64(a2)*ratio) - return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -} - - -func getColorCubicRow(img image.Image, x, y, offset float64) image.Color { - c0 := img.At(int(x), int(y)) - c1 := img.At(int(x+1), int(y)) - c2 := img.At(int(x+2), int(y)) - c3 := img.At(int(x+3), int(y)) - r0, g0, b0, a0 := c0.RGBA() - 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)) - 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 { - x0 := math.Floor(x) - y0 := math.Floor(y) - dx := x - x0 - dy := y - y0 - - c0 := getColorCubicRow(img, x0-1, y0-1, dx) - c1 := getColorCubicRow(img, x0-1, y0, dx) - c2 := getColorCubicRow(img, x0-1, y0+1, dx) - c3 := getColorCubicRow(img, x0-1, y0+2, dx) - r0, g0, b0, a0 := c0.RGBA() - 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)) - 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 - 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 { - 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 - return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -} func (gc *ImageGraphicContext) DrawImage(img image.Image) { - width := float64(gc.img.Bounds().Dx()) - height := float64(gc.img.Bounds().Dy()) - gc.current.tr.Transform(&width, &height) - var x, y, u, v float64 - for x = 0; x < width; x++ { - for y = 0; y < height; y++ { - u = x - v = y - gc.current.tr.InverseTransform(&u, &v) - gc.img.Set(int(x), int(y), compose(gc.img.At(int(x), int(y)), getColorLinear(img, u, v))) - } - } + DrawImage(img, gc.img, gc.current.tr, draw.Over, linearFilter) } func (gc *ImageGraphicContext) BeginPath() { diff --git a/draw2d/rgba_interpolation.go b/draw2d/rgba_interpolation.go new file mode 100644 index 0000000..81683f1 --- /dev/null +++ b/draw2d/rgba_interpolation.go @@ -0,0 +1,118 @@ +package draw2d + +import ( + "exp/draw" + "image" + "math" +) +type ImageFilter int + +const ( + linearFilter ImageFilter = iota + bilinearFilter + bicubicFilter +) + +//see http://pippin.gimp.org/image_processing/chap_resampling.html +func getColorLinear(img image.Image, x, y float64) image.Color { + return img.At(int(x), int(y)) +} + +func getColorBilinear(img image.Image, x, y float64) image.Color { + x0 := math.Floor(x) + y0 := math.Floor(y) + dx := x - x0 + dy := y - y0 + + color0 := img.At(int(x0), int(y0)) + color1 := img.At(int(x0+1), int(y0)) + color2 := img.At(int(x0+1), int(y0+1)) + color3 := img.At(int(x0), int(y0+1)) + + return lerp(lerp(color0, color1, dx), lerp(color3, color2, dx), dy) +} +/** +-- LERP +-- /lerp/, vi.,n. +-- +-- Quasi-acronym for Linear Interpolation, used as a verb or noun for +-- the operation. "Bresenham's algorithm lerps incrementally between the +-- two endpoints of the line." (From Jargon File (4.4.4, 14 Aug 2003) +*/ +func lerp(c1, c2 image.Color, ratio float64) image.Color { + r1, g1, b1, a1 := c1.RGBA() + 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) + a := int(float64(a1)*(1-ratio) + float64(a2)*ratio) + return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} +} + + +func getColorCubicRow(img image.Image, x, y, offset float64) image.Color { + c0 := img.At(int(x), int(y)) + c1 := img.At(int(x+1), int(y)) + c2 := img.At(int(x+2), int(y)) + c3 := img.At(int(x+3), int(y)) + r0, g0, b0, a0 := c0.RGBA() + 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)) + 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 { + x0 := math.Floor(x) + y0 := math.Floor(y) + dx := x - x0 + dy := y - y0 + + c0 := getColorCubicRow(img, x0-1, y0-1, dx) + c1 := getColorCubicRow(img, x0-1, y0, dx) + c2 := getColorCubicRow(img, x0-1, y0+1, dx) + c3 := getColorCubicRow(img, x0-1, y0+2, dx) + r0, g0, b0, a0 := c0.RGBA() + 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)) + 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 + 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 { + 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 + 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()) + var x, y, u, v float64 + for x = 0; x < width; x++ { + for y = 0; y < height; 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))) + } + } +} diff --git a/resource/result/TestDrawImage.png b/resource/result/TestDrawImage.png index f3a686c..62fb065 100644 Binary files a/resource/result/TestDrawImage.png and b/resource/result/TestDrawImage.png differ