Fix wrong boundary calculation (bug #17)

This fix doesn't currently work concurrently. This will be fixed in another commit.
This commit is contained in:
jst 2014-08-20 20:54:59 +02:00
parent 03982f698d
commit e950449c49
4 changed files with 121 additions and 149 deletions

View File

@ -41,28 +41,26 @@ func clampUint16(in int64) uint16 {
} }
func resizeGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) { func resizeGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Bounds().Dx(), in.Bounds().Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
maxX := in.Bounds().Dx() - 1
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var rgba [4]int64 var rgba [4]int64
var sum int64 var sum int64
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
coeff := coeffs[ci+i] coeff := coeffs[ci+i]
if coeff != 0 { if coeff != 0 {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
break xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = oldBounds.Min.X xi = maxX
default:
xi = oldBounds.Max.X - 1
} }
r, g, b, a := in.At(xi, x).RGBA() r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
rgba[0] += int64(coeff) * int64(r) rgba[0] += int64(coeff) * int64(r)
rgba[1] += int64(coeff) * int64(g) rgba[1] += int64(coeff) * int64(g)
rgba[2] += int64(coeff) * int64(b) rgba[2] += int64(coeff) * int64(b)
@ -89,29 +87,27 @@ func resizeGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []in
} }
func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) { func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X * 4 maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 4
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var rgba [4]int32 var rgba [4]int32
var sum int32 var sum int32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
coeff := coeffs[ci+i] coeff := coeffs[ci+i]
if coeff != 0 { if coeff != 0 {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
xi *= 4 xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = 4 * maxX
default: default:
xi = minX xi *= 4
} }
rgba[0] += int32(coeff) * int32(row[xi+0]) rgba[0] += int32(coeff) * int32(row[xi+0])
rgba[1] += int32(coeff) * int32(row[xi+1]) rgba[1] += int32(coeff) * int32(row[xi+1])
@ -131,29 +127,27 @@ func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16,
} }
func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) { func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X * 8 maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 8
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var rgba [4]int64 var rgba [4]int64
var sum int64 var sum int64
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
coeff := coeffs[ci+i] coeff := coeffs[ci+i]
if coeff != 0 { if coeff != 0 {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
xi *= 8 xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = 8 * maxX
default: default:
xi = minX xi *= 8
} }
rgba[0] += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1])) rgba[0] += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
rgba[1] += int64(coeff) * int64(uint16(row[xi+2])<<8|uint16(row[xi+3])) rgba[1] += int64(coeff) * int64(uint16(row[xi+2])<<8|uint16(row[xi+3]))
@ -181,29 +175,25 @@ func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []i
} }
func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) { func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1)
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var gray int32 var gray int32
var sum int32 var sum int32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
coeff := coeffs[ci+i] coeff := coeffs[ci+i]
if coeff != 0 { if coeff != 0 {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
break xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = maxX
default:
xi = minX
} }
gray += int32(coeff) * int32(row[xi]) gray += int32(coeff) * int32(row[xi])
sum += int32(coeff) sum += int32(coeff)
@ -217,29 +207,27 @@ func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16,
} }
func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, offset []int, filterLength int) { func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X * 2 maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 2
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var gray int64 var gray int64
var sum int64 var sum int64
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
coeff := coeffs[ci+i] coeff := coeffs[ci+i]
if coeff != 0 { if coeff != 0 {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
xi *= 2 xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = 2 * maxX
default: default:
xi = minX xi *= 2
} }
gray += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1])) gray += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
sum += int64(coeff) sum += int64(coeff)
@ -255,29 +243,27 @@ func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []i
} }
func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int, filterLength int) { func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X * 3 maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 3
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var p [3]int32 var p [3]int32
var sum int32 var sum int32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
coeff := coeffs[ci+i] coeff := coeffs[ci+i]
if coeff != 0 { if coeff != 0 {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
xi *= 3 xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = 3 * maxX
default: default:
xi = minX xi *= 3
} }
p[0] += int32(coeff) * int32(row[xi+0]) p[0] += int32(coeff) * int32(row[xi+0])
p[1] += int32(coeff) * int32(row[xi+1]) p[1] += int32(coeff) * int32(row[xi+1])
@ -295,28 +281,26 @@ func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int,
} }
func nearestYCbCr(in *ycc, out *ycc, scale float64, coeffs []bool, offset []int, filterLength int) { func nearestYCbCr(in *ycc, out *ycc, scale float64, coeffs []bool, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X * 3 maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 3
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var p [3]float32 var p [3]float32
var sum float32 var sum float32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
if coeffs[ci+i] { if coeffs[ci+i] {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
xi *= 3 xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = 3 * maxX
default: default:
xi = minX xi *= 3
} }
p[0] += float32(row[xi+0]) p[0] += float32(row[xi+0])
p[1] += float32(row[xi+1]) p[1] += float32(row[xi+1])

View File

@ -80,14 +80,14 @@ func lanczos3(in float64) float64 {
} }
// range [-256,256] // range [-256,256]
func createWeights8(dy, minx, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int16, []int, int) { func createWeights8(dy, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int16, []int, int) {
filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1)) filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
filterFactor := math.Min(1./(blur*scale), 1) filterFactor := math.Min(1./(blur*scale), 1)
coeffs := make([]int16, dy*filterLength) coeffs := make([]int16, dy*filterLength)
start := make([]int, dy) start := make([]int, dy)
for y := 0; y < dy; y++ { for y := 0; y < dy; y++ {
interpX := scale*(float64(y)+0.5) + float64(minx) interpX := scale * (float64(y) + 0.5)
start[y] = int(interpX) - filterLength/2 + 1 start[y] = int(interpX) - filterLength/2 + 1
interpX -= float64(start[y]) interpX -= float64(start[y])
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
@ -100,14 +100,14 @@ func createWeights8(dy, minx, filterLength int, blur, scale float64, kernel func
} }
// range [-65536,65536] // range [-65536,65536]
func createWeights16(dy, minx, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int32, []int, int) { func createWeights16(dy, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int32, []int, int) {
filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1)) filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
filterFactor := math.Min(1./(blur*scale), 1) filterFactor := math.Min(1./(blur*scale), 1)
coeffs := make([]int32, dy*filterLength) coeffs := make([]int32, dy*filterLength)
start := make([]int, dy) start := make([]int, dy)
for y := 0; y < dy; y++ { for y := 0; y < dy; y++ {
interpX := scale*(float64(y)+0.5) + float64(minx) interpX := scale * (float64(y) + 0.5)
start[y] = int(interpX) - filterLength/2 + 1 start[y] = int(interpX) - filterLength/2 + 1
interpX -= float64(start[y]) interpX -= float64(start[y])
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
@ -119,14 +119,14 @@ func createWeights16(dy, minx, filterLength int, blur, scale float64, kernel fun
return coeffs, start, filterLength return coeffs, start, filterLength
} }
func createWeightsNearest(dy, minx, filterLength int, blur, scale float64) ([]bool, []int, int) { func createWeightsNearest(dy, filterLength int, blur, scale float64) ([]bool, []int, int) {
filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1)) filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
filterFactor := math.Min(1./(blur*scale), 1) filterFactor := math.Min(1./(blur*scale), 1)
coeffs := make([]bool, dy*filterLength) coeffs := make([]bool, dy*filterLength)
start := make([]int, dy) start := make([]int, dy)
for y := 0; y < dy; y++ { for y := 0; y < dy; y++ {
interpX := scale*(float64(y)+0.5) + float64(minx) interpX := scale * (float64(y) + 0.5)
start[y] = int(interpX) - filterLength/2 + 1 start[y] = int(interpX) - filterLength/2 + 1
interpX -= float64(start[y]) interpX -= float64(start[y])
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {

View File

@ -35,27 +35,25 @@ func floatToUint16(x float32) uint16 {
} }
func nearestGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) { func nearestGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Bounds().Dx(), in.Bounds().Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
maxX := in.Bounds().Dx() - 1
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var rgba [4]float32 var rgba [4]float32
var sum float32 var sum float32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
if coeffs[ci+i] { if coeffs[ci+i] {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
break xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = oldBounds.Min.X xi = maxX
default:
xi = oldBounds.Max.X - 1
} }
r, g, b, a := in.At(xi, x).RGBA() r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
rgba[0] += float32(r) rgba[0] += float32(r)
rgba[1] += float32(g) rgba[1] += float32(g)
rgba[2] += float32(b) rgba[2] += float32(b)
@ -82,28 +80,26 @@ func nearestGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []b
} }
func nearestRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []bool, offset []int, filterLength int) { func nearestRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X * 4 maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 4
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var rgba [4]float32 var rgba [4]float32
var sum float32 var sum float32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
if coeffs[ci+i] { if coeffs[ci+i] {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
xi *= 4 xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = 4 * maxX
default: default:
xi = minX xi *= 4
} }
rgba[0] += float32(row[xi+0]) rgba[0] += float32(row[xi+0])
rgba[1] += float32(row[xi+1]) rgba[1] += float32(row[xi+1])
@ -123,28 +119,26 @@ func nearestRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []bool,
} }
func nearestRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) { func nearestRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X * 8 maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 8
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var rgba [4]float32 var rgba [4]float32
var sum float32 var sum float32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
if coeffs[ci+i] { if coeffs[ci+i] {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
xi *= 8 xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = 8 * maxX
default: default:
xi = minX xi *= 8
} }
rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1])) rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3])) rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
@ -172,28 +166,24 @@ func nearestRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []
} }
func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool, offset []int, filterLength int) { func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1)
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var gray float32 var gray float32
var sum float32 var sum float32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
if coeffs[ci+i] { if coeffs[ci+i] {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
break xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = maxX
default:
xi = minX
} }
gray += float32(row[xi]) gray += float32(row[xi])
sum++ sum++
@ -207,28 +197,26 @@ func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool,
} }
func nearestGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []bool, offset []int, filterLength int) { func nearestGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []bool, offset []int, filterLength int) {
oldBounds := image.Rect(0, 0, in.Rect.Dx(), in.Rect.Dy())
newBounds := out.Bounds() newBounds := out.Bounds()
minX := oldBounds.Min.X * 2 maxX := in.Bounds().Dx() - 1
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 2
for x := newBounds.Min.X; x < newBounds.Max.X; x++ { for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:] row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
var gray float32 var gray float32
var sum float32 var sum float32
start := offset[y] start := offset[y-newBounds.Min.Y]
ci := (y - newBounds.Min.Y) * filterLength ci := (y - newBounds.Min.Y) * filterLength
for i := 0; i < filterLength; i++ { for i := 0; i < filterLength; i++ {
if coeffs[ci+i] { if coeffs[ci+i] {
xi := start + i xi := start + i
switch { switch {
case uint(xi) < uint(oldBounds.Max.X): case xi < 0:
xi *= 2 xi = 0
case xi >= oldBounds.Max.X: case xi >= maxX:
xi = maxX xi = 2 * maxX
default: default:
xi = minX xi *= 2
} }
gray += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1])) gray += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
sum++ sum++

View File

@ -91,7 +91,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
} }
taps, kernel := interp.kernel() taps, kernel := interp.kernel()
cpus := runtime.NumCPU() cpus := 1 //runtime.NumCPU() TODO
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
// Generic access to image.Image is slow in tight loops. // Generic access to image.Image is slow in tight loops.
@ -103,7 +103,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
result := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel) coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.RGBA) slice := makeSlice(temp, i, cpus).(*image.RGBA)
@ -115,7 +115,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel) coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.RGBA) slice := makeSlice(result, i, cpus).(*image.RGBA)
@ -133,7 +133,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio) temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio) result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio)
coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel) coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
in := imageYCbCrToYCC(input) in := imageYCbCrToYCC(input)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
@ -145,7 +145,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
} }
wg.Wait() wg.Wait()
coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel) coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*ycc) slice := makeSlice(result, i, cpus).(*ycc)
@ -162,7 +162,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel) coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.RGBA64) slice := makeSlice(temp, i, cpus).(*image.RGBA64)
@ -174,7 +174,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel) coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.RGBA64) slice := makeSlice(result, i, cpus).(*image.RGBA64)
@ -191,7 +191,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
result := image.NewGray(image.Rect(0, 0, int(width), int(height))) result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel) coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.Gray) slice := makeSlice(temp, i, cpus).(*image.Gray)
@ -203,7 +203,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel) coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.Gray) slice := makeSlice(result, i, cpus).(*image.Gray)
@ -220,7 +220,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
result := image.NewGray16(image.Rect(0, 0, int(width), int(height))) result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel) coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.Gray16) slice := makeSlice(temp, i, cpus).(*image.Gray16)
@ -232,7 +232,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel) coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.Gray16) slice := makeSlice(result, i, cpus).(*image.Gray16)
@ -249,7 +249,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), img.Bounds().Min.X, taps, blur, scaleX, kernel) coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.RGBA64) slice := makeSlice(temp, i, cpus).(*image.RGBA64)
@ -261,7 +261,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel) coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.RGBA64) slice := makeSlice(result, i, cpus).(*image.RGBA64)
@ -287,7 +287,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
result := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX) coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.RGBA) slice := makeSlice(temp, i, cpus).(*image.RGBA)
@ -299,7 +299,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY) coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.RGBA) slice := makeSlice(result, i, cpus).(*image.RGBA)
@ -317,7 +317,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio) temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio) result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio)
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX) coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
in := imageYCbCrToYCC(input) in := imageYCbCrToYCC(input)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
@ -329,7 +329,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
} }
wg.Wait() wg.Wait()
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY) coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*ycc) slice := makeSlice(result, i, cpus).(*ycc)
@ -346,7 +346,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX) coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.RGBA64) slice := makeSlice(temp, i, cpus).(*image.RGBA64)
@ -358,7 +358,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY) coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.RGBA64) slice := makeSlice(result, i, cpus).(*image.RGBA64)
@ -375,7 +375,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
result := image.NewGray(image.Rect(0, 0, int(width), int(height))) result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX) coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.Gray) slice := makeSlice(temp, i, cpus).(*image.Gray)
@ -387,7 +387,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY) coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.Gray) slice := makeSlice(result, i, cpus).(*image.Gray)
@ -404,7 +404,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
result := image.NewGray16(image.Rect(0, 0, int(width), int(height))) result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX) coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.Gray16) slice := makeSlice(temp, i, cpus).(*image.Gray16)
@ -416,7 +416,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY) coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.Gray16) slice := makeSlice(result, i, cpus).(*image.Gray16)
@ -433,7 +433,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
// horizontal filter, results in transposed temporary image // horizontal filter, results in transposed temporary image
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), img.Bounds().Min.X, taps, blur, scaleX) coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(temp, i, cpus).(*image.RGBA64) slice := makeSlice(temp, i, cpus).(*image.RGBA64)
@ -445,7 +445,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
wg.Wait() wg.Wait()
// horizontal filter on transposed image, result is not transposed // horizontal filter on transposed image, result is not transposed
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY) coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
wg.Add(cpus) wg.Add(cpus)
for i := 0; i < cpus; i++ { for i := 0; i < cpus; i++ {
slice := makeSlice(result, i, cpus).(*image.RGBA64) slice := makeSlice(result, i, cpus).(*image.RGBA64)