From 001a24bc17599c815aa05cc089153244a32e3ad3 Mon Sep 17 00:00:00 2001 From: Chris Rhodes Date: Fri, 1 Jan 2016 23:32:37 -0800 Subject: [PATCH] Replace DrawImage implementation for draw.Scaler. This change has no API modifications. --- draw2dimg/ftgc.go | 33 ++++++- draw2dimg/rgba_interpolation.go | 151 -------------------------------- 2 files changed, 32 insertions(+), 152 deletions(-) delete mode 100644 draw2dimg/rgba_interpolation.go diff --git a/draw2dimg/ftgc.go b/draw2dimg/ftgc.go index f0ebf03..f40e19a 100644 --- a/draw2dimg/ftgc.go +++ b/draw2dimg/ftgc.go @@ -7,7 +7,6 @@ import ( "errors" "image" "image/color" - "image/draw" "log" "math" @@ -17,6 +16,7 @@ import ( "github.com/golang/freetype/raster" "github.com/golang/freetype/truetype" + "golang.org/x/image/draw" "golang.org/x/image/font" "golang.org/x/image/math/fixed" ) @@ -38,6 +38,18 @@ type GraphicContext struct { DPI int } +// ImageFilter defines the type of filter to use +type ImageFilter int + +const ( + // LinearFilter defines a linear filter + LinearFilter ImageFilter = iota + // BilinearFilter defines a bilinear filter + BilinearFilter + // BicubicFilter defines a bicubic filter + BicubicFilter +) + // NewGraphicContext creates a new Graphic context from an image. func NewGraphicContext(img draw.Image) *GraphicContext { @@ -84,6 +96,25 @@ func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) { draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP, draw.Over) } +// DrawImage draws an image into dest using an affine transformation matrix, an op and a filter +func DrawImage(src image.Image, dest draw.Image, tr draw2d.Matrix, op draw.Op, filter ImageFilter) { + srcRect := src.Bounds() + x0, y0, x1, y1 := tr.TransformRectangle(float64(srcRect.Min.X), float64(srcRect.Min.Y), float64(srcRect.Max.X), float64(srcRect.Max.Y)) + destRect := image.Rectangle{image.Point{int(x0), int(y0)}, image.Point{int(x1), int(y1)}} + + var scaler draw.Scaler + switch filter { + case LinearFilter: + scaler = draw.NearestNeighbor + case BilinearFilter: + scaler = draw.BiLinear + case BicubicFilter: + scaler = draw.CatmullRom + } + + scaler.Scale(dest, destRect, src, srcRect, draw.Over, nil) +} + // DrawImage draws the raster image in the current canvas func (gc *GraphicContext) DrawImage(img image.Image) { DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter) diff --git a/draw2dimg/rgba_interpolation.go b/draw2dimg/rgba_interpolation.go deleted file mode 100644 index 6a7c740..0000000 --- a/draw2dimg/rgba_interpolation.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff -// see http://pippin.gimp.org/image_processing/chap_resampling.html - -package draw2dimg - -import ( - "image" - "image/color" - "image/draw" - "math" - - "github.com/llgcode/draw2d" -) - -// ImageFilter defines the type of filter to use -type ImageFilter int - -const ( - // LinearFilter defines a linear filter - LinearFilter ImageFilter = iota - // BilinearFilter defines a bilinear filter - BilinearFilter - // BicubicFilter defines a bicubic filter - BicubicFilter - // M is the maximum value for a rgb component - M = 1<<16 - 1 -) - -//see http://pippin.gimp.org/image_processing/chap_resampling.html -func getColorLinear(img image.Image, x, y float64) color.Color { - return img.At(int(x), int(y)) -} - -func getColorBilinear(img image.Image, x, y float64) color.Color { - x0 := math.Floor(x) - y0 := math.Floor(y) - dx := x - x0 - dy := y - y0 - - rt, gt, bt, at := img.At(int(x0), int(y0)).RGBA() - r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = img.At(int(x0+1), int(y0)).RGBA() - r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = img.At(int(x0+1), int(y0+1)).RGBA() - r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = img.At(int(x0), int(y0+1)).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 color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -} - -// lerp is a linear interpolation bertween 2 points -func lerp(v1, v2, ratio float64) float64 { - return v1*(1-ratio) + v2*ratio -} - -func getColorCubicRow(img image.Image, x, y, offset float64) color.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)) - rt, gt, bt, at := c0.RGBA() - 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, 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) - return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -} - -func getColorBicubic(img image.Image, x, y float64) color.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) - rt, gt, bt, at := c0.RGBA() - 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, 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 color.RGBA{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) -} - -// DrawImage draws an image into dest using an affine transformation matrix, an op and a filter -func DrawImage(src image.Image, dest draw.Image, tr draw2d.Matrix, op draw.Op, filter ImageFilter) { - bounds := src.Bounds() - x0, y0, x1, y1 := tr.TransformRectangle(float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y)) - var x, y, u, v float64 - var c1, c2, cr color.Color - var r, g, b, a, ia, r1, g1, b1, a1, r2, g2, b2, a2 uint32 - var color color.RGBA - for x = x0; x < x1; x++ { - for y = y0; y < y1; y++ { - u = x - v = y - u, v = tr.InverseTransformPoint(u, v) - if bounds.Min.X <= int(u) && bounds.Max.X > int(u) && bounds.Min.Y <= int(v) && bounds.Max.Y > int(v) { - c1 = dest.At(int(x), int(y)) - switch filter { - case LinearFilter: - c2 = src.At(int(u), int(v)) - case BilinearFilter: - c2 = getColorBilinear(src, u, v) - case BicubicFilter: - c2 = getColorBicubic(src, u, v) - } - 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 - color.R = uint8(r >> 8) - color.G = uint8(g >> 8) - color.B = uint8(b >> 8) - color.A = uint8(a >> 8) - cr = color - default: - cr = c2 - } - dest.Set(int(x), int(y), cr) - } - } - } -}