diff --git a/filters.go b/filters.go index 3bf29d2..27b57ec 100644 --- a/filters.go +++ b/filters.go @@ -142,6 +142,35 @@ func createFilter(img image.Image, factor [2]float32, size int, kernel func(floa return } +// Return a filter kernel that performs nearly identically to the provided +// kernel, but generates and uses a precomputed table rather than executing +// the kernel for each evaluation. The table is generated with tableSize +// values that cover the kernal domain from -maxX to +maxX. The input kernel +// is assumed to be symmetrical around 0, so the table only includes values +// from 0 to maxX. +func tableKernel(kernel func(float32) float32, tableSize int, + maxX float32) func(float32) float32 { + + // precompute an array of filter coefficients + weights := make([]float32, tableSize+1) + for i := range weights { + weights[i] = kernel(maxX * float32(i) / float32(tableSize)) + } + weights[tableSize] = 0.0 + + return func(x float32) float32 { + if x < 0.0 { + x = -x + } + indf := x / maxX * float32(tableSize) + ind := int(indf) + if ind >= tableSize { + return 0.0 + } + return weights[ind] + (weights[ind+1]-weights[ind])*(indf-float32(ind)) + } +} + // Nearest-neighbor interpolation func NearestNeighbor(img image.Image, factor [2]float32) Filter { return createFilter(img, factor, 2, func(x float32) (y float32) { @@ -213,12 +242,16 @@ func lanczosKernel(a uint) func(float32) float32 { } } +const lanczosTableSize = 300 + // Lanczos interpolation (a=2) func Lanczos2(img image.Image, factor [2]float32) Filter { - return createFilter(img, factor, 4, lanczosKernel(2)) + return createFilter(img, factor, 4, + tableKernel(lanczosKernel(2), lanczosTableSize, 2.0)) } // Lanczos interpolation (a=3) func Lanczos3(img image.Image, factor [2]float32) Filter { - return createFilter(img, factor, 6, lanczosKernel(3)) + return createFilter(img, factor, 6, + tableKernel(lanczosKernel(3), lanczosTableSize, 3.0)) }