golang-image/draw/impl.go

4645 lines
141 KiB
Go
Raw Normal View History

// generated by "go run gen.go". DO NOT EDIT.
package draw
import (
"image"
"image/color"
"math"
"golang.org/x/image/math/f64"
)
func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
// adr is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
if adr.Empty() || sr.Empty() {
return
}
// sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking.
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
if !sr.In(src.Bounds()) {
z.scale_Image_Image(dst, dr, adr, src, sr)
} else if _, ok := src.(*image.Uniform); ok {
// TODO: get the Op from opts.
Draw(dst, dr, src, src.Bounds().Min, Src)
} else {
switch dst := dst.(type) {
case *image.RGBA:
switch src := src.(type) {
case *image.Gray:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_RGBA_Gray(dst, dr, adr, src, sr)
case *image.NRGBA:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_RGBA_NRGBA(dst, dr, adr, src, sr)
case *image.RGBA:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_RGBA_RGBA(dst, dr, adr, src, sr)
case *image.YCbCr:
switch src.SubsampleRatio {
default:
z.scale_RGBA_Image(dst, dr, adr, src, sr)
case image.YCbCrSubsampleRatio444:
z.scale_RGBA_YCbCr444(dst, dr, adr, src, sr)
case image.YCbCrSubsampleRatio422:
z.scale_RGBA_YCbCr422(dst, dr, adr, src, sr)
case image.YCbCrSubsampleRatio420:
z.scale_RGBA_YCbCr420(dst, dr, adr, src, sr)
case image.YCbCrSubsampleRatio440:
z.scale_RGBA_YCbCr440(dst, dr, adr, src, sr)
}
default:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_RGBA_Image(dst, dr, adr, src, sr)
}
default:
switch src := src.(type) {
default:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_Image_Image(dst, dr, adr, src, sr)
}
}
}
}
func (z nnInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) {
dr := transformRect(s2d, &sr)
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
if adr.Empty() || sr.Empty() {
return
}
d2s := invert(s2d)
// bias is a translation of the mapping from dst co-ordinates to
// src co-ordinates such that the latter temporarily have
// non-negative X and Y co-ordinates. This allows us to write
// int(f) instead of int(math.Floor(f)), since "round to zero" and
// "round down" are equivalent when f >= 0, but the former is much
// cheaper. The X-- and Y-- are because the TransformLeaf methods
// have a "sx -= 0.5" adjustment.
bias := transformRect(&d2s, &adr).Min
bias.X--
bias.Y--
d2s[2] -= float64(bias.X)
d2s[5] -= float64(bias.Y)
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
// sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking.
if !sr.In(src.Bounds()) {
z.transform_Image_Image(dst, dr, adr, &d2s, src, sr, bias)
} else if u, ok := src.(*image.Uniform); ok {
// TODO: get the Op from opts.
transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, Src)
} else {
switch dst := dst.(type) {
case *image.RGBA:
switch src := src.(type) {
case *image.Gray:
z.transform_RGBA_Gray(dst, dr, adr, &d2s, src, sr, bias)
case *image.NRGBA:
z.transform_RGBA_NRGBA(dst, dr, adr, &d2s, src, sr, bias)
case *image.RGBA:
z.transform_RGBA_RGBA(dst, dr, adr, &d2s, src, sr, bias)
case *image.YCbCr:
switch src.SubsampleRatio {
default:
z.transform_RGBA_Image(dst, dr, adr, &d2s, src, sr, bias)
case image.YCbCrSubsampleRatio444:
z.transform_RGBA_YCbCr444(dst, dr, adr, &d2s, src, sr, bias)
case image.YCbCrSubsampleRatio422:
z.transform_RGBA_YCbCr422(dst, dr, adr, &d2s, src, sr, bias)
case image.YCbCrSubsampleRatio420:
z.transform_RGBA_YCbCr420(dst, dr, adr, &d2s, src, sr, bias)
case image.YCbCrSubsampleRatio440:
z.transform_RGBA_YCbCr440(dst, dr, adr, &d2s, src, sr, bias)
}
default:
z.transform_RGBA_Image(dst, dr, adr, &d2s, src, sr, bias)
}
default:
switch src := src.(type) {
default:
z.transform_Image_Image(dst, dr, adr, &d2s, src, sr, bias)
}
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (nnInterpolator) scale_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, src *image.Gray, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sy := (2*uint64(dy) + 1) * sh / dh2
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sx := (2*uint64(dx) + 1) * sw / dw2
pi := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.Stride + (sr.Min.X + int(sx) - src.Rect.Min.X)
pr := uint32(src.Pix[pi]) * 0x101
out := uint8(uint32(pr) >> 8)
dst.Pix[d+0] = out
dst.Pix[d+1] = out
dst.Pix[d+2] = out
dst.Pix[d+3] = 0xff
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (nnInterpolator) scale_RGBA_NRGBA(dst *image.RGBA, dr, adr image.Rectangle, src *image.NRGBA, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sy := (2*uint64(dy) + 1) * sh / dh2
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sx := (2*uint64(dx) + 1) * sw / dw2
pi := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx)-src.Rect.Min.X)*4
pa := uint32(src.Pix[pi+3]) * 0x101
pr := uint32(src.Pix[pi+0]) * pa / 0xff
pg := uint32(src.Pix[pi+1]) * pa / 0xff
pb := uint32(src.Pix[pi+2]) * pa / 0xff
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = uint8(uint32(pa) >> 8)
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (nnInterpolator) scale_RGBA_RGBA(dst *image.RGBA, dr, adr image.Rectangle, src *image.RGBA, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sy := (2*uint64(dy) + 1) * sh / dh2
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sx := (2*uint64(dx) + 1) * sw / dw2
pi := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx)-src.Rect.Min.X)*4
pr := uint32(src.Pix[pi+0]) * 0x101
pg := uint32(src.Pix[pi+1]) * 0x101
pb := uint32(src.Pix[pi+2]) * 0x101
pa := uint32(src.Pix[pi+3]) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = uint8(uint32(pa) >> 8)
}
}
}
func (nnInterpolator) scale_RGBA_YCbCr444(dst *image.RGBA, dr, adr image.Rectangle, src *image.YCbCr, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (2*uint64(dy) + 1) * sh / dh2
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (2*uint64(dx) + 1) * sw / dw2
pi := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx) - src.Rect.Min.X)
pj := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.CStride + (sr.Min.X + int(sx) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pr := uint32(pr8) * 0x101
pg := uint32(pg8) * 0x101
pb := uint32(pb8) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (nnInterpolator) scale_RGBA_YCbCr422(dst *image.RGBA, dr, adr image.Rectangle, src *image.YCbCr, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (2*uint64(dy) + 1) * sh / dh2
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (2*uint64(dx) + 1) * sw / dw2
pi := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx) - src.Rect.Min.X)
pj := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.CStride + ((sr.Min.X+int(sx))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pr := uint32(pr8) * 0x101
pg := uint32(pg8) * 0x101
pb := uint32(pb8) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (nnInterpolator) scale_RGBA_YCbCr420(dst *image.RGBA, dr, adr image.Rectangle, src *image.YCbCr, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (2*uint64(dy) + 1) * sh / dh2
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (2*uint64(dx) + 1) * sw / dw2
pi := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx) - src.Rect.Min.X)
pj := ((sr.Min.Y+int(sy))/2-src.Rect.Min.Y/2)*src.CStride + ((sr.Min.X+int(sx))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pr := uint32(pr8) * 0x101
pg := uint32(pg8) * 0x101
pb := uint32(pb8) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (nnInterpolator) scale_RGBA_YCbCr440(dst *image.RGBA, dr, adr image.Rectangle, src *image.YCbCr, sr image.Rectangle) {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sy := (2*uint64(dy) + 1) * sh / dh2
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sx := (2*uint64(dx) + 1) * sw / dw2
pi := (sr.Min.Y+int(sy)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx) - src.Rect.Min.X)
pj := ((sr.Min.Y+int(sy))/2-src.Rect.Min.Y/2)*src.CStride + (sr.Min.X + int(sx) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pr := uint32(pr8) * 0x101
pg := uint32(pg8) * 0x101
pb := uint32(pb8) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (nnInterpolator) scale_RGBA_Image(dst *image.RGBA, dr, adr image.Rectangle, src image.Image, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sy := (2*uint64(dy) + 1) * sh / dh2
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sx := (2*uint64(dx) + 1) * sw / dw2
pr, pg, pb, pa := src.At(sr.Min.X+int(sx), sr.Min.Y+int(sy)).RGBA()
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = uint8(uint32(pa) >> 8)
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (nnInterpolator) scale_Image_Image(dst Image, dr, adr image.Rectangle, src image.Image, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 2
sw := uint64(sr.Dx())
sh := uint64(sr.Dy())
dstColorRGBA64 := &color.RGBA64{}
dstColor := color.Color(dstColorRGBA64)
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sy := (2*uint64(dy) + 1) * sh / dh2
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sx := (2*uint64(dx) + 1) * sw / dw2
pr, pg, pb, pa := src.At(sr.Min.X+int(sx), sr.Min.Y+int(sy)).RGBA()
dstColorRGBA64.R = uint16(pr)
dstColorRGBA64.G = uint16(pg)
dstColorRGBA64.B = uint16(pb)
dstColorRGBA64.A = uint16(pa)
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
}
}
}
func (nnInterpolator) transform_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.Gray, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pi := (sy0-src.Rect.Min.Y)*src.Stride + (sx0 - src.Rect.Min.X)
pr := uint32(src.Pix[pi]) * 0x101
out := uint8(uint32(pr) >> 8)
dst.Pix[d+0] = out
dst.Pix[d+1] = out
dst.Pix[d+2] = out
dst.Pix[d+3] = 0xff
}
}
}
func (nnInterpolator) transform_RGBA_NRGBA(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.NRGBA, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pi := (sy0-src.Rect.Min.Y)*src.Stride + (sx0-src.Rect.Min.X)*4
pa := uint32(src.Pix[pi+3]) * 0x101
pr := uint32(src.Pix[pi+0]) * pa / 0xff
pg := uint32(src.Pix[pi+1]) * pa / 0xff
pb := uint32(src.Pix[pi+2]) * pa / 0xff
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = uint8(uint32(pa) >> 8)
}
}
}
func (nnInterpolator) transform_RGBA_RGBA(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.RGBA, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pi := (sy0-src.Rect.Min.Y)*src.Stride + (sx0-src.Rect.Min.X)*4
pr := uint32(src.Pix[pi+0]) * 0x101
pg := uint32(src.Pix[pi+1]) * 0x101
pb := uint32(src.Pix[pi+2]) * 0x101
pa := uint32(src.Pix[pi+3]) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = uint8(uint32(pa) >> 8)
}
}
}
func (nnInterpolator) transform_RGBA_YCbCr444(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pi := (sy0-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
pj := (sy0-src.Rect.Min.Y)*src.CStride + (sx0 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pr := uint32(pr8) * 0x101
pg := uint32(pg8) * 0x101
pb := uint32(pb8) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (nnInterpolator) transform_RGBA_YCbCr422(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pi := (sy0-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
pj := (sy0-src.Rect.Min.Y)*src.CStride + ((sx0)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pr := uint32(pr8) * 0x101
pg := uint32(pg8) * 0x101
pb := uint32(pb8) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (nnInterpolator) transform_RGBA_YCbCr420(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pi := (sy0-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
pj := ((sy0)/2-src.Rect.Min.Y/2)*src.CStride + ((sx0)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pr := uint32(pr8) * 0x101
pg := uint32(pg8) * 0x101
pb := uint32(pb8) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (nnInterpolator) transform_RGBA_YCbCr440(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pi := (sy0-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
pj := ((sy0)/2-src.Rect.Min.Y/2)*src.CStride + (sx0 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pr := uint32(pr8) * 0x101
pg := uint32(pg8) * 0x101
pb := uint32(pb8) * 0x101
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (nnInterpolator) transform_RGBA_Image(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pr, pg, pb, pa := src.At(sx0, sy0).RGBA()
dst.Pix[d+0] = uint8(uint32(pr) >> 8)
dst.Pix[d+1] = uint8(uint32(pg) >> 8)
dst.Pix[d+2] = uint8(uint32(pb) >> 8)
dst.Pix[d+3] = uint8(uint32(pa) >> 8)
}
}
}
func (nnInterpolator) transform_Image_Image(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point) {
dstColorRGBA64 := &color.RGBA64{}
dstColor := color.Color(dstColorRGBA64)
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
if !(image.Point{sx0, sy0}).In(sr) {
continue
}
pr, pg, pb, pa := src.At(sx0, sy0).RGBA()
dstColorRGBA64.R = uint16(pr)
dstColorRGBA64.G = uint16(pg)
dstColorRGBA64.B = uint16(pb)
dstColorRGBA64.A = uint16(pa)
dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
}
}
}
func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
// adr is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
if adr.Empty() || sr.Empty() {
return
}
// sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking.
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
if !sr.In(src.Bounds()) {
z.scale_Image_Image(dst, dr, adr, src, sr)
} else if _, ok := src.(*image.Uniform); ok {
// TODO: get the Op from opts.
Draw(dst, dr, src, src.Bounds().Min, Src)
} else {
switch dst := dst.(type) {
case *image.RGBA:
switch src := src.(type) {
case *image.Gray:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_RGBA_Gray(dst, dr, adr, src, sr)
case *image.NRGBA:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_RGBA_NRGBA(dst, dr, adr, src, sr)
case *image.RGBA:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_RGBA_RGBA(dst, dr, adr, src, sr)
case *image.YCbCr:
switch src.SubsampleRatio {
default:
z.scale_RGBA_Image(dst, dr, adr, src, sr)
case image.YCbCrSubsampleRatio444:
z.scale_RGBA_YCbCr444(dst, dr, adr, src, sr)
case image.YCbCrSubsampleRatio422:
z.scale_RGBA_YCbCr422(dst, dr, adr, src, sr)
case image.YCbCrSubsampleRatio420:
z.scale_RGBA_YCbCr420(dst, dr, adr, src, sr)
case image.YCbCrSubsampleRatio440:
z.scale_RGBA_YCbCr440(dst, dr, adr, src, sr)
}
default:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_RGBA_Image(dst, dr, adr, src, sr)
}
default:
switch src := src.(type) {
default:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scale_Image_Image(dst, dr, adr, src, sr)
}
}
}
}
func (z ablInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) {
dr := transformRect(s2d, &sr)
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
if adr.Empty() || sr.Empty() {
return
}
d2s := invert(s2d)
// bias is a translation of the mapping from dst co-ordinates to
// src co-ordinates such that the latter temporarily have
// non-negative X and Y co-ordinates. This allows us to write
// int(f) instead of int(math.Floor(f)), since "round to zero" and
// "round down" are equivalent when f >= 0, but the former is much
// cheaper. The X-- and Y-- are because the TransformLeaf methods
// have a "sx -= 0.5" adjustment.
bias := transformRect(&d2s, &adr).Min
bias.X--
bias.Y--
d2s[2] -= float64(bias.X)
d2s[5] -= float64(bias.Y)
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
// sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking.
if !sr.In(src.Bounds()) {
z.transform_Image_Image(dst, dr, adr, &d2s, src, sr, bias)
} else if u, ok := src.(*image.Uniform); ok {
// TODO: get the Op from opts.
transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, Src)
} else {
switch dst := dst.(type) {
case *image.RGBA:
switch src := src.(type) {
case *image.Gray:
z.transform_RGBA_Gray(dst, dr, adr, &d2s, src, sr, bias)
case *image.NRGBA:
z.transform_RGBA_NRGBA(dst, dr, adr, &d2s, src, sr, bias)
case *image.RGBA:
z.transform_RGBA_RGBA(dst, dr, adr, &d2s, src, sr, bias)
case *image.YCbCr:
switch src.SubsampleRatio {
default:
z.transform_RGBA_Image(dst, dr, adr, &d2s, src, sr, bias)
case image.YCbCrSubsampleRatio444:
z.transform_RGBA_YCbCr444(dst, dr, adr, &d2s, src, sr, bias)
case image.YCbCrSubsampleRatio422:
z.transform_RGBA_YCbCr422(dst, dr, adr, &d2s, src, sr, bias)
case image.YCbCrSubsampleRatio420:
z.transform_RGBA_YCbCr420(dst, dr, adr, &d2s, src, sr, bias)
case image.YCbCrSubsampleRatio440:
z.transform_RGBA_YCbCr440(dst, dr, adr, &d2s, src, sr, bias)
}
default:
z.transform_RGBA_Image(dst, dr, adr, &d2s, src, sr, bias)
}
default:
switch src := src.(type) {
default:
z.transform_Image_Image(dst, dr, adr, &d2s, src, sr, bias)
}
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (ablInterpolator) scale_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, src *image.Gray, sr image.Rectangle) {
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.Stride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s00ru := uint32(src.Pix[s00i]) * 0x101
s00r := float64(s00ru)
s10i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.Stride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s10ru := uint32(src.Pix[s10i]) * 0x101
s10r := float64(s10ru)
s10r = xFrac1*s00r + xFrac0*s10r
s01i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.Stride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s01ru := uint32(src.Pix[s01i]) * 0x101
s01r := float64(s01ru)
s11i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.Stride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s11ru := uint32(src.Pix[s11i]) * 0x101
s11r := float64(s11ru)
s11r = xFrac1*s01r + xFrac0*s11r
s11r = yFrac1*s10r + yFrac0*s11r
out := uint8(uint32(s11r) >> 8)
dst.Pix[d+0] = out
dst.Pix[d+1] = out
dst.Pix[d+2] = out
dst.Pix[d+3] = 0xff
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (ablInterpolator) scale_RGBA_NRGBA(dst *image.RGBA, dr, adr image.Rectangle, src *image.NRGBA, sr image.Rectangle) {
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx0)-src.Rect.Min.X)*4
s00au := uint32(src.Pix[s00i+3]) * 0x101
s00ru := uint32(src.Pix[s00i+0]) * s00au / 0xff
s00gu := uint32(src.Pix[s00i+1]) * s00au / 0xff
s00bu := uint32(src.Pix[s00i+2]) * s00au / 0xff
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s00a := float64(s00au)
s10i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx1)-src.Rect.Min.X)*4
s10au := uint32(src.Pix[s10i+3]) * 0x101
s10ru := uint32(src.Pix[s10i+0]) * s10au / 0xff
s10gu := uint32(src.Pix[s10i+1]) * s10au / 0xff
s10bu := uint32(src.Pix[s10i+2]) * s10au / 0xff
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10a := float64(s10au)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s10a = xFrac1*s00a + xFrac0*s10a
s01i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx0)-src.Rect.Min.X)*4
s01au := uint32(src.Pix[s01i+3]) * 0x101
s01ru := uint32(src.Pix[s01i+0]) * s01au / 0xff
s01gu := uint32(src.Pix[s01i+1]) * s01au / 0xff
s01bu := uint32(src.Pix[s01i+2]) * s01au / 0xff
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s01a := float64(s01au)
s11i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx1)-src.Rect.Min.X)*4
s11au := uint32(src.Pix[s11i+3]) * 0x101
s11ru := uint32(src.Pix[s11i+0]) * s11au / 0xff
s11gu := uint32(src.Pix[s11i+1]) * s11au / 0xff
s11bu := uint32(src.Pix[s11i+2]) * s11au / 0xff
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11a := float64(s11au)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11a = xFrac1*s01a + xFrac0*s11a
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
s11a = yFrac1*s10a + yFrac0*s11a
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = uint8(uint32(s11a) >> 8)
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (ablInterpolator) scale_RGBA_RGBA(dst *image.RGBA, dr, adr image.Rectangle, src *image.RGBA, sr image.Rectangle) {
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx0)-src.Rect.Min.X)*4
s00ru := uint32(src.Pix[s00i+0]) * 0x101
s00gu := uint32(src.Pix[s00i+1]) * 0x101
s00bu := uint32(src.Pix[s00i+2]) * 0x101
s00au := uint32(src.Pix[s00i+3]) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s00a := float64(s00au)
s10i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx1)-src.Rect.Min.X)*4
s10ru := uint32(src.Pix[s10i+0]) * 0x101
s10gu := uint32(src.Pix[s10i+1]) * 0x101
s10bu := uint32(src.Pix[s10i+2]) * 0x101
s10au := uint32(src.Pix[s10i+3]) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10a := float64(s10au)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s10a = xFrac1*s00a + xFrac0*s10a
s01i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx0)-src.Rect.Min.X)*4
s01ru := uint32(src.Pix[s01i+0]) * 0x101
s01gu := uint32(src.Pix[s01i+1]) * 0x101
s01bu := uint32(src.Pix[s01i+2]) * 0x101
s01au := uint32(src.Pix[s01i+3]) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s01a := float64(s01au)
s11i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(sx1)-src.Rect.Min.X)*4
s11ru := uint32(src.Pix[s11i+0]) * 0x101
s11gu := uint32(src.Pix[s11i+1]) * 0x101
s11bu := uint32(src.Pix[s11i+2]) * 0x101
s11au := uint32(src.Pix[s11i+3]) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11a := float64(s11au)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11a = xFrac1*s01a + xFrac0*s11a
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
s11a = yFrac1*s10a + yFrac0*s11a
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = uint8(uint32(s11a) >> 8)
}
}
}
func (ablInterpolator) scale_RGBA_YCbCr444(dst *image.RGBA, dr, adr image.Rectangle, src *image.YCbCr, sr image.Rectangle) {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s00j := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.CStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s00yy1 := int(src.Y[s00i])<<16 + 1<<15
s00cb1 := int(src.Cb[s00j]) - 128
s00cr1 := int(src.Cr[s00j]) - 128
s00r8 := (s00yy1 + 91881*s00cr1) >> 16
s00g8 := (s00yy1 - 22554*s00cb1 - 46802*s00cr1) >> 16
s00b8 := (s00yy1 + 116130*s00cb1) >> 16
if s00r8 < 0 {
s00r8 = 0
} else if s00r8 > 0xff {
s00r8 = 0xff
}
if s00g8 < 0 {
s00g8 = 0
} else if s00g8 > 0xff {
s00g8 = 0xff
}
if s00b8 < 0 {
s00b8 = 0
} else if s00b8 > 0xff {
s00b8 = 0xff
}
s00ru := uint32(s00r8) * 0x101
s00gu := uint32(s00g8) * 0x101
s00bu := uint32(s00b8) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s10i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s10j := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.CStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s10yy1 := int(src.Y[s10i])<<16 + 1<<15
s10cb1 := int(src.Cb[s10j]) - 128
s10cr1 := int(src.Cr[s10j]) - 128
s10r8 := (s10yy1 + 91881*s10cr1) >> 16
s10g8 := (s10yy1 - 22554*s10cb1 - 46802*s10cr1) >> 16
s10b8 := (s10yy1 + 116130*s10cb1) >> 16
if s10r8 < 0 {
s10r8 = 0
} else if s10r8 > 0xff {
s10r8 = 0xff
}
if s10g8 < 0 {
s10g8 = 0
} else if s10g8 > 0xff {
s10g8 = 0xff
}
if s10b8 < 0 {
s10b8 = 0
} else if s10b8 > 0xff {
s10b8 = 0xff
}
s10ru := uint32(s10r8) * 0x101
s10gu := uint32(s10g8) * 0x101
s10bu := uint32(s10b8) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s01i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s01j := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.CStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s01yy1 := int(src.Y[s01i])<<16 + 1<<15
s01cb1 := int(src.Cb[s01j]) - 128
s01cr1 := int(src.Cr[s01j]) - 128
s01r8 := (s01yy1 + 91881*s01cr1) >> 16
s01g8 := (s01yy1 - 22554*s01cb1 - 46802*s01cr1) >> 16
s01b8 := (s01yy1 + 116130*s01cb1) >> 16
if s01r8 < 0 {
s01r8 = 0
} else if s01r8 > 0xff {
s01r8 = 0xff
}
if s01g8 < 0 {
s01g8 = 0
} else if s01g8 > 0xff {
s01g8 = 0xff
}
if s01b8 < 0 {
s01b8 = 0
} else if s01b8 > 0xff {
s01b8 = 0xff
}
s01ru := uint32(s01r8) * 0x101
s01gu := uint32(s01g8) * 0x101
s01bu := uint32(s01b8) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s11i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s11j := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.CStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s11yy1 := int(src.Y[s11i])<<16 + 1<<15
s11cb1 := int(src.Cb[s11j]) - 128
s11cr1 := int(src.Cr[s11j]) - 128
s11r8 := (s11yy1 + 91881*s11cr1) >> 16
s11g8 := (s11yy1 - 22554*s11cb1 - 46802*s11cr1) >> 16
s11b8 := (s11yy1 + 116130*s11cb1) >> 16
if s11r8 < 0 {
s11r8 = 0
} else if s11r8 > 0xff {
s11r8 = 0xff
}
if s11g8 < 0 {
s11g8 = 0
} else if s11g8 > 0xff {
s11g8 = 0xff
}
if s11b8 < 0 {
s11b8 = 0
} else if s11b8 > 0xff {
s11b8 = 0xff
}
s11ru := uint32(s11r8) * 0x101
s11gu := uint32(s11g8) * 0x101
s11bu := uint32(s11b8) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) scale_RGBA_YCbCr422(dst *image.RGBA, dr, adr image.Rectangle, src *image.YCbCr, sr image.Rectangle) {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s00j := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.CStride + ((sr.Min.X+int(sx0))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s00yy1 := int(src.Y[s00i])<<16 + 1<<15
s00cb1 := int(src.Cb[s00j]) - 128
s00cr1 := int(src.Cr[s00j]) - 128
s00r8 := (s00yy1 + 91881*s00cr1) >> 16
s00g8 := (s00yy1 - 22554*s00cb1 - 46802*s00cr1) >> 16
s00b8 := (s00yy1 + 116130*s00cb1) >> 16
if s00r8 < 0 {
s00r8 = 0
} else if s00r8 > 0xff {
s00r8 = 0xff
}
if s00g8 < 0 {
s00g8 = 0
} else if s00g8 > 0xff {
s00g8 = 0xff
}
if s00b8 < 0 {
s00b8 = 0
} else if s00b8 > 0xff {
s00b8 = 0xff
}
s00ru := uint32(s00r8) * 0x101
s00gu := uint32(s00g8) * 0x101
s00bu := uint32(s00b8) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s10i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s10j := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.CStride + ((sr.Min.X+int(sx1))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s10yy1 := int(src.Y[s10i])<<16 + 1<<15
s10cb1 := int(src.Cb[s10j]) - 128
s10cr1 := int(src.Cr[s10j]) - 128
s10r8 := (s10yy1 + 91881*s10cr1) >> 16
s10g8 := (s10yy1 - 22554*s10cb1 - 46802*s10cr1) >> 16
s10b8 := (s10yy1 + 116130*s10cb1) >> 16
if s10r8 < 0 {
s10r8 = 0
} else if s10r8 > 0xff {
s10r8 = 0xff
}
if s10g8 < 0 {
s10g8 = 0
} else if s10g8 > 0xff {
s10g8 = 0xff
}
if s10b8 < 0 {
s10b8 = 0
} else if s10b8 > 0xff {
s10b8 = 0xff
}
s10ru := uint32(s10r8) * 0x101
s10gu := uint32(s10g8) * 0x101
s10bu := uint32(s10b8) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s01i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s01j := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.CStride + ((sr.Min.X+int(sx0))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s01yy1 := int(src.Y[s01i])<<16 + 1<<15
s01cb1 := int(src.Cb[s01j]) - 128
s01cr1 := int(src.Cr[s01j]) - 128
s01r8 := (s01yy1 + 91881*s01cr1) >> 16
s01g8 := (s01yy1 - 22554*s01cb1 - 46802*s01cr1) >> 16
s01b8 := (s01yy1 + 116130*s01cb1) >> 16
if s01r8 < 0 {
s01r8 = 0
} else if s01r8 > 0xff {
s01r8 = 0xff
}
if s01g8 < 0 {
s01g8 = 0
} else if s01g8 > 0xff {
s01g8 = 0xff
}
if s01b8 < 0 {
s01b8 = 0
} else if s01b8 > 0xff {
s01b8 = 0xff
}
s01ru := uint32(s01r8) * 0x101
s01gu := uint32(s01g8) * 0x101
s01bu := uint32(s01b8) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s11i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s11j := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.CStride + ((sr.Min.X+int(sx1))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s11yy1 := int(src.Y[s11i])<<16 + 1<<15
s11cb1 := int(src.Cb[s11j]) - 128
s11cr1 := int(src.Cr[s11j]) - 128
s11r8 := (s11yy1 + 91881*s11cr1) >> 16
s11g8 := (s11yy1 - 22554*s11cb1 - 46802*s11cr1) >> 16
s11b8 := (s11yy1 + 116130*s11cb1) >> 16
if s11r8 < 0 {
s11r8 = 0
} else if s11r8 > 0xff {
s11r8 = 0xff
}
if s11g8 < 0 {
s11g8 = 0
} else if s11g8 > 0xff {
s11g8 = 0xff
}
if s11b8 < 0 {
s11b8 = 0
} else if s11b8 > 0xff {
s11b8 = 0xff
}
s11ru := uint32(s11r8) * 0x101
s11gu := uint32(s11g8) * 0x101
s11bu := uint32(s11b8) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) scale_RGBA_YCbCr420(dst *image.RGBA, dr, adr image.Rectangle, src *image.YCbCr, sr image.Rectangle) {
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s00j := ((sr.Min.Y+int(sy0))/2-src.Rect.Min.Y/2)*src.CStride + ((sr.Min.X+int(sx0))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s00yy1 := int(src.Y[s00i])<<16 + 1<<15
s00cb1 := int(src.Cb[s00j]) - 128
s00cr1 := int(src.Cr[s00j]) - 128
s00r8 := (s00yy1 + 91881*s00cr1) >> 16
s00g8 := (s00yy1 - 22554*s00cb1 - 46802*s00cr1) >> 16
s00b8 := (s00yy1 + 116130*s00cb1) >> 16
if s00r8 < 0 {
s00r8 = 0
} else if s00r8 > 0xff {
s00r8 = 0xff
}
if s00g8 < 0 {
s00g8 = 0
} else if s00g8 > 0xff {
s00g8 = 0xff
}
if s00b8 < 0 {
s00b8 = 0
} else if s00b8 > 0xff {
s00b8 = 0xff
}
s00ru := uint32(s00r8) * 0x101
s00gu := uint32(s00g8) * 0x101
s00bu := uint32(s00b8) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s10i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s10j := ((sr.Min.Y+int(sy0))/2-src.Rect.Min.Y/2)*src.CStride + ((sr.Min.X+int(sx1))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s10yy1 := int(src.Y[s10i])<<16 + 1<<15
s10cb1 := int(src.Cb[s10j]) - 128
s10cr1 := int(src.Cr[s10j]) - 128
s10r8 := (s10yy1 + 91881*s10cr1) >> 16
s10g8 := (s10yy1 - 22554*s10cb1 - 46802*s10cr1) >> 16
s10b8 := (s10yy1 + 116130*s10cb1) >> 16
if s10r8 < 0 {
s10r8 = 0
} else if s10r8 > 0xff {
s10r8 = 0xff
}
if s10g8 < 0 {
s10g8 = 0
} else if s10g8 > 0xff {
s10g8 = 0xff
}
if s10b8 < 0 {
s10b8 = 0
} else if s10b8 > 0xff {
s10b8 = 0xff
}
s10ru := uint32(s10r8) * 0x101
s10gu := uint32(s10g8) * 0x101
s10bu := uint32(s10b8) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s01i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s01j := ((sr.Min.Y+int(sy1))/2-src.Rect.Min.Y/2)*src.CStride + ((sr.Min.X+int(sx0))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s01yy1 := int(src.Y[s01i])<<16 + 1<<15
s01cb1 := int(src.Cb[s01j]) - 128
s01cr1 := int(src.Cr[s01j]) - 128
s01r8 := (s01yy1 + 91881*s01cr1) >> 16
s01g8 := (s01yy1 - 22554*s01cb1 - 46802*s01cr1) >> 16
s01b8 := (s01yy1 + 116130*s01cb1) >> 16
if s01r8 < 0 {
s01r8 = 0
} else if s01r8 > 0xff {
s01r8 = 0xff
}
if s01g8 < 0 {
s01g8 = 0
} else if s01g8 > 0xff {
s01g8 = 0xff
}
if s01b8 < 0 {
s01b8 = 0
} else if s01b8 > 0xff {
s01b8 = 0xff
}
s01ru := uint32(s01r8) * 0x101
s01gu := uint32(s01g8) * 0x101
s01bu := uint32(s01b8) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s11i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s11j := ((sr.Min.Y+int(sy1))/2-src.Rect.Min.Y/2)*src.CStride + ((sr.Min.X+int(sx1))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s11yy1 := int(src.Y[s11i])<<16 + 1<<15
s11cb1 := int(src.Cb[s11j]) - 128
s11cr1 := int(src.Cr[s11j]) - 128
s11r8 := (s11yy1 + 91881*s11cr1) >> 16
s11g8 := (s11yy1 - 22554*s11cb1 - 46802*s11cr1) >> 16
s11b8 := (s11yy1 + 116130*s11cb1) >> 16
if s11r8 < 0 {
s11r8 = 0
} else if s11r8 > 0xff {
s11r8 = 0xff
}
if s11g8 < 0 {
s11g8 = 0
} else if s11g8 > 0xff {
s11g8 = 0xff
}
if s11b8 < 0 {
s11b8 = 0
} else if s11b8 > 0xff {
s11b8 = 0xff
}
s11ru := uint32(s11r8) * 0x101
s11gu := uint32(s11g8) * 0x101
s11bu := uint32(s11b8) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) scale_RGBA_YCbCr440(dst *image.RGBA, dr, adr image.Rectangle, src *image.YCbCr, sr image.Rectangle) {
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s00j := ((sr.Min.Y+int(sy0))/2-src.Rect.Min.Y/2)*src.CStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s00yy1 := int(src.Y[s00i])<<16 + 1<<15
s00cb1 := int(src.Cb[s00j]) - 128
s00cr1 := int(src.Cr[s00j]) - 128
s00r8 := (s00yy1 + 91881*s00cr1) >> 16
s00g8 := (s00yy1 - 22554*s00cb1 - 46802*s00cr1) >> 16
s00b8 := (s00yy1 + 116130*s00cb1) >> 16
if s00r8 < 0 {
s00r8 = 0
} else if s00r8 > 0xff {
s00r8 = 0xff
}
if s00g8 < 0 {
s00g8 = 0
} else if s00g8 > 0xff {
s00g8 = 0xff
}
if s00b8 < 0 {
s00b8 = 0
} else if s00b8 > 0xff {
s00b8 = 0xff
}
s00ru := uint32(s00r8) * 0x101
s00gu := uint32(s00g8) * 0x101
s00bu := uint32(s00b8) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s10i := (sr.Min.Y+int(sy0)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s10j := ((sr.Min.Y+int(sy0))/2-src.Rect.Min.Y/2)*src.CStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s10yy1 := int(src.Y[s10i])<<16 + 1<<15
s10cb1 := int(src.Cb[s10j]) - 128
s10cr1 := int(src.Cr[s10j]) - 128
s10r8 := (s10yy1 + 91881*s10cr1) >> 16
s10g8 := (s10yy1 - 22554*s10cb1 - 46802*s10cr1) >> 16
s10b8 := (s10yy1 + 116130*s10cb1) >> 16
if s10r8 < 0 {
s10r8 = 0
} else if s10r8 > 0xff {
s10r8 = 0xff
}
if s10g8 < 0 {
s10g8 = 0
} else if s10g8 > 0xff {
s10g8 = 0xff
}
if s10b8 < 0 {
s10b8 = 0
} else if s10b8 > 0xff {
s10b8 = 0xff
}
s10ru := uint32(s10r8) * 0x101
s10gu := uint32(s10g8) * 0x101
s10bu := uint32(s10b8) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s01i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
s01j := ((sr.Min.Y+int(sy1))/2-src.Rect.Min.Y/2)*src.CStride + (sr.Min.X + int(sx0) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s01yy1 := int(src.Y[s01i])<<16 + 1<<15
s01cb1 := int(src.Cb[s01j]) - 128
s01cr1 := int(src.Cr[s01j]) - 128
s01r8 := (s01yy1 + 91881*s01cr1) >> 16
s01g8 := (s01yy1 - 22554*s01cb1 - 46802*s01cr1) >> 16
s01b8 := (s01yy1 + 116130*s01cb1) >> 16
if s01r8 < 0 {
s01r8 = 0
} else if s01r8 > 0xff {
s01r8 = 0xff
}
if s01g8 < 0 {
s01g8 = 0
} else if s01g8 > 0xff {
s01g8 = 0xff
}
if s01b8 < 0 {
s01b8 = 0
} else if s01b8 > 0xff {
s01b8 = 0xff
}
s01ru := uint32(s01r8) * 0x101
s01gu := uint32(s01g8) * 0x101
s01bu := uint32(s01b8) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s11i := (sr.Min.Y+int(sy1)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
s11j := ((sr.Min.Y+int(sy1))/2-src.Rect.Min.Y/2)*src.CStride + (sr.Min.X + int(sx1) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s11yy1 := int(src.Y[s11i])<<16 + 1<<15
s11cb1 := int(src.Cb[s11j]) - 128
s11cr1 := int(src.Cr[s11j]) - 128
s11r8 := (s11yy1 + 91881*s11cr1) >> 16
s11g8 := (s11yy1 - 22554*s11cb1 - 46802*s11cr1) >> 16
s11b8 := (s11yy1 + 116130*s11cb1) >> 16
if s11r8 < 0 {
s11r8 = 0
} else if s11r8 > 0xff {
s11r8 = 0xff
}
if s11g8 < 0 {
s11g8 = 0
} else if s11g8 > 0xff {
s11g8 = 0xff
}
if s11b8 < 0 {
s11b8 = 0
} else if s11b8 > 0xff {
s11b8 = 0xff
}
s11ru := uint32(s11r8) * 0x101
s11gu := uint32(s11g8) * 0x101
s11bu := uint32(s11b8) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) scale_RGBA_Image(dst *image.RGBA, dr, adr image.Rectangle, src image.Image, sr image.Rectangle) {
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00ru, s00gu, s00bu, s00au := src.At(sr.Min.X+int(sx0), sr.Min.Y+int(sy0)).RGBA()
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s00a := float64(s00au)
s10ru, s10gu, s10bu, s10au := src.At(sr.Min.X+int(sx1), sr.Min.Y+int(sy0)).RGBA()
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10a := float64(s10au)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s10a = xFrac1*s00a + xFrac0*s10a
s01ru, s01gu, s01bu, s01au := src.At(sr.Min.X+int(sx0), sr.Min.Y+int(sy1)).RGBA()
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s01a := float64(s01au)
s11ru, s11gu, s11bu, s11au := src.At(sr.Min.X+int(sx1), sr.Min.Y+int(sy1)).RGBA()
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11a := float64(s11au)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11a = xFrac1*s01a + xFrac0*s11a
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
s11a = yFrac1*s10a + yFrac0*s11a
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = uint8(uint32(s11a) >> 8)
}
}
}
func (ablInterpolator) scale_Image_Image(dst Image, dr, adr image.Rectangle, src image.Image, sr image.Rectangle) {
sw := int32(sr.Dx())
sh := int32(sr.Dy())
yscale := float64(sh) / float64(dr.Dy())
xscale := float64(sw) / float64(dr.Dx())
swMinus1, shMinus1 := sw-1, sh-1
dstColorRGBA64 := &color.RGBA64{}
dstColor := color.Color(dstColorRGBA64)
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
sy := (float64(dy)+0.5)*yscale - 0.5
// If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
// we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
// sx, below.
sy0 := int32(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy1 := sy0 + 1
if sy < 0 {
sy0, sy1 = 0, 0
yFrac0, yFrac1 = 0, 1
} else if sy1 > shMinus1 {
sy0, sy1 = shMinus1, shMinus1
yFrac0, yFrac1 = 1, 0
}
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
sx := (float64(dx)+0.5)*xscale - 0.5
sx0 := int32(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx1 := sx0 + 1
if sx < 0 {
sx0, sx1 = 0, 0
xFrac0, xFrac1 = 0, 1
} else if sx1 > swMinus1 {
sx0, sx1 = swMinus1, swMinus1
xFrac0, xFrac1 = 1, 0
}
s00ru, s00gu, s00bu, s00au := src.At(sr.Min.X+int(sx0), sr.Min.Y+int(sy0)).RGBA()
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s00a := float64(s00au)
s10ru, s10gu, s10bu, s10au := src.At(sr.Min.X+int(sx1), sr.Min.Y+int(sy0)).RGBA()
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10a := float64(s10au)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s10a = xFrac1*s00a + xFrac0*s10a
s01ru, s01gu, s01bu, s01au := src.At(sr.Min.X+int(sx0), sr.Min.Y+int(sy1)).RGBA()
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s01a := float64(s01au)
s11ru, s11gu, s11bu, s11au := src.At(sr.Min.X+int(sx1), sr.Min.Y+int(sy1)).RGBA()
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11a := float64(s11au)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11a = xFrac1*s01a + xFrac0*s11a
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
s11a = yFrac1*s10a + yFrac0*s11a
dstColorRGBA64.R = uint16(s11r)
dstColorRGBA64.G = uint16(s11g)
dstColorRGBA64.B = uint16(s11b)
dstColorRGBA64.A = uint16(s11a)
dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
}
}
}
func (ablInterpolator) transform_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.Gray, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00i := (sy0-src.Rect.Min.Y)*src.Stride + (sx0 - src.Rect.Min.X)
s00ru := uint32(src.Pix[s00i]) * 0x101
s00r := float64(s00ru)
s10i := (sy0-src.Rect.Min.Y)*src.Stride + (sx1 - src.Rect.Min.X)
s10ru := uint32(src.Pix[s10i]) * 0x101
s10r := float64(s10ru)
s10r = xFrac1*s00r + xFrac0*s10r
s01i := (sy1-src.Rect.Min.Y)*src.Stride + (sx0 - src.Rect.Min.X)
s01ru := uint32(src.Pix[s01i]) * 0x101
s01r := float64(s01ru)
s11i := (sy1-src.Rect.Min.Y)*src.Stride + (sx1 - src.Rect.Min.X)
s11ru := uint32(src.Pix[s11i]) * 0x101
s11r := float64(s11ru)
s11r = xFrac1*s01r + xFrac0*s11r
s11r = yFrac1*s10r + yFrac0*s11r
out := uint8(uint32(s11r) >> 8)
dst.Pix[d+0] = out
dst.Pix[d+1] = out
dst.Pix[d+2] = out
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) transform_RGBA_NRGBA(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.NRGBA, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00i := (sy0-src.Rect.Min.Y)*src.Stride + (sx0-src.Rect.Min.X)*4
s00au := uint32(src.Pix[s00i+3]) * 0x101
s00ru := uint32(src.Pix[s00i+0]) * s00au / 0xff
s00gu := uint32(src.Pix[s00i+1]) * s00au / 0xff
s00bu := uint32(src.Pix[s00i+2]) * s00au / 0xff
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s00a := float64(s00au)
s10i := (sy0-src.Rect.Min.Y)*src.Stride + (sx1-src.Rect.Min.X)*4
s10au := uint32(src.Pix[s10i+3]) * 0x101
s10ru := uint32(src.Pix[s10i+0]) * s10au / 0xff
s10gu := uint32(src.Pix[s10i+1]) * s10au / 0xff
s10bu := uint32(src.Pix[s10i+2]) * s10au / 0xff
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10a := float64(s10au)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s10a = xFrac1*s00a + xFrac0*s10a
s01i := (sy1-src.Rect.Min.Y)*src.Stride + (sx0-src.Rect.Min.X)*4
s01au := uint32(src.Pix[s01i+3]) * 0x101
s01ru := uint32(src.Pix[s01i+0]) * s01au / 0xff
s01gu := uint32(src.Pix[s01i+1]) * s01au / 0xff
s01bu := uint32(src.Pix[s01i+2]) * s01au / 0xff
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s01a := float64(s01au)
s11i := (sy1-src.Rect.Min.Y)*src.Stride + (sx1-src.Rect.Min.X)*4
s11au := uint32(src.Pix[s11i+3]) * 0x101
s11ru := uint32(src.Pix[s11i+0]) * s11au / 0xff
s11gu := uint32(src.Pix[s11i+1]) * s11au / 0xff
s11bu := uint32(src.Pix[s11i+2]) * s11au / 0xff
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11a := float64(s11au)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11a = xFrac1*s01a + xFrac0*s11a
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
s11a = yFrac1*s10a + yFrac0*s11a
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = uint8(uint32(s11a) >> 8)
}
}
}
func (ablInterpolator) transform_RGBA_RGBA(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.RGBA, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00i := (sy0-src.Rect.Min.Y)*src.Stride + (sx0-src.Rect.Min.X)*4
s00ru := uint32(src.Pix[s00i+0]) * 0x101
s00gu := uint32(src.Pix[s00i+1]) * 0x101
s00bu := uint32(src.Pix[s00i+2]) * 0x101
s00au := uint32(src.Pix[s00i+3]) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s00a := float64(s00au)
s10i := (sy0-src.Rect.Min.Y)*src.Stride + (sx1-src.Rect.Min.X)*4
s10ru := uint32(src.Pix[s10i+0]) * 0x101
s10gu := uint32(src.Pix[s10i+1]) * 0x101
s10bu := uint32(src.Pix[s10i+2]) * 0x101
s10au := uint32(src.Pix[s10i+3]) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10a := float64(s10au)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s10a = xFrac1*s00a + xFrac0*s10a
s01i := (sy1-src.Rect.Min.Y)*src.Stride + (sx0-src.Rect.Min.X)*4
s01ru := uint32(src.Pix[s01i+0]) * 0x101
s01gu := uint32(src.Pix[s01i+1]) * 0x101
s01bu := uint32(src.Pix[s01i+2]) * 0x101
s01au := uint32(src.Pix[s01i+3]) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s01a := float64(s01au)
s11i := (sy1-src.Rect.Min.Y)*src.Stride + (sx1-src.Rect.Min.X)*4
s11ru := uint32(src.Pix[s11i+0]) * 0x101
s11gu := uint32(src.Pix[s11i+1]) * 0x101
s11bu := uint32(src.Pix[s11i+2]) * 0x101
s11au := uint32(src.Pix[s11i+3]) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11a := float64(s11au)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11a = xFrac1*s01a + xFrac0*s11a
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
s11a = yFrac1*s10a + yFrac0*s11a
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = uint8(uint32(s11a) >> 8)
}
}
}
func (ablInterpolator) transform_RGBA_YCbCr444(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00i := (sy0-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
s00j := (sy0-src.Rect.Min.Y)*src.CStride + (sx0 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s00yy1 := int(src.Y[s00i])<<16 + 1<<15
s00cb1 := int(src.Cb[s00j]) - 128
s00cr1 := int(src.Cr[s00j]) - 128
s00r8 := (s00yy1 + 91881*s00cr1) >> 16
s00g8 := (s00yy1 - 22554*s00cb1 - 46802*s00cr1) >> 16
s00b8 := (s00yy1 + 116130*s00cb1) >> 16
if s00r8 < 0 {
s00r8 = 0
} else if s00r8 > 0xff {
s00r8 = 0xff
}
if s00g8 < 0 {
s00g8 = 0
} else if s00g8 > 0xff {
s00g8 = 0xff
}
if s00b8 < 0 {
s00b8 = 0
} else if s00b8 > 0xff {
s00b8 = 0xff
}
s00ru := uint32(s00r8) * 0x101
s00gu := uint32(s00g8) * 0x101
s00bu := uint32(s00b8) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s10i := (sy0-src.Rect.Min.Y)*src.YStride + (sx1 - src.Rect.Min.X)
s10j := (sy0-src.Rect.Min.Y)*src.CStride + (sx1 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s10yy1 := int(src.Y[s10i])<<16 + 1<<15
s10cb1 := int(src.Cb[s10j]) - 128
s10cr1 := int(src.Cr[s10j]) - 128
s10r8 := (s10yy1 + 91881*s10cr1) >> 16
s10g8 := (s10yy1 - 22554*s10cb1 - 46802*s10cr1) >> 16
s10b8 := (s10yy1 + 116130*s10cb1) >> 16
if s10r8 < 0 {
s10r8 = 0
} else if s10r8 > 0xff {
s10r8 = 0xff
}
if s10g8 < 0 {
s10g8 = 0
} else if s10g8 > 0xff {
s10g8 = 0xff
}
if s10b8 < 0 {
s10b8 = 0
} else if s10b8 > 0xff {
s10b8 = 0xff
}
s10ru := uint32(s10r8) * 0x101
s10gu := uint32(s10g8) * 0x101
s10bu := uint32(s10b8) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s01i := (sy1-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
s01j := (sy1-src.Rect.Min.Y)*src.CStride + (sx0 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s01yy1 := int(src.Y[s01i])<<16 + 1<<15
s01cb1 := int(src.Cb[s01j]) - 128
s01cr1 := int(src.Cr[s01j]) - 128
s01r8 := (s01yy1 + 91881*s01cr1) >> 16
s01g8 := (s01yy1 - 22554*s01cb1 - 46802*s01cr1) >> 16
s01b8 := (s01yy1 + 116130*s01cb1) >> 16
if s01r8 < 0 {
s01r8 = 0
} else if s01r8 > 0xff {
s01r8 = 0xff
}
if s01g8 < 0 {
s01g8 = 0
} else if s01g8 > 0xff {
s01g8 = 0xff
}
if s01b8 < 0 {
s01b8 = 0
} else if s01b8 > 0xff {
s01b8 = 0xff
}
s01ru := uint32(s01r8) * 0x101
s01gu := uint32(s01g8) * 0x101
s01bu := uint32(s01b8) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s11i := (sy1-src.Rect.Min.Y)*src.YStride + (sx1 - src.Rect.Min.X)
s11j := (sy1-src.Rect.Min.Y)*src.CStride + (sx1 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s11yy1 := int(src.Y[s11i])<<16 + 1<<15
s11cb1 := int(src.Cb[s11j]) - 128
s11cr1 := int(src.Cr[s11j]) - 128
s11r8 := (s11yy1 + 91881*s11cr1) >> 16
s11g8 := (s11yy1 - 22554*s11cb1 - 46802*s11cr1) >> 16
s11b8 := (s11yy1 + 116130*s11cb1) >> 16
if s11r8 < 0 {
s11r8 = 0
} else if s11r8 > 0xff {
s11r8 = 0xff
}
if s11g8 < 0 {
s11g8 = 0
} else if s11g8 > 0xff {
s11g8 = 0xff
}
if s11b8 < 0 {
s11b8 = 0
} else if s11b8 > 0xff {
s11b8 = 0xff
}
s11ru := uint32(s11r8) * 0x101
s11gu := uint32(s11g8) * 0x101
s11bu := uint32(s11b8) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) transform_RGBA_YCbCr422(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00i := (sy0-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
s00j := (sy0-src.Rect.Min.Y)*src.CStride + ((sx0)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s00yy1 := int(src.Y[s00i])<<16 + 1<<15
s00cb1 := int(src.Cb[s00j]) - 128
s00cr1 := int(src.Cr[s00j]) - 128
s00r8 := (s00yy1 + 91881*s00cr1) >> 16
s00g8 := (s00yy1 - 22554*s00cb1 - 46802*s00cr1) >> 16
s00b8 := (s00yy1 + 116130*s00cb1) >> 16
if s00r8 < 0 {
s00r8 = 0
} else if s00r8 > 0xff {
s00r8 = 0xff
}
if s00g8 < 0 {
s00g8 = 0
} else if s00g8 > 0xff {
s00g8 = 0xff
}
if s00b8 < 0 {
s00b8 = 0
} else if s00b8 > 0xff {
s00b8 = 0xff
}
s00ru := uint32(s00r8) * 0x101
s00gu := uint32(s00g8) * 0x101
s00bu := uint32(s00b8) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s10i := (sy0-src.Rect.Min.Y)*src.YStride + (sx1 - src.Rect.Min.X)
s10j := (sy0-src.Rect.Min.Y)*src.CStride + ((sx1)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s10yy1 := int(src.Y[s10i])<<16 + 1<<15
s10cb1 := int(src.Cb[s10j]) - 128
s10cr1 := int(src.Cr[s10j]) - 128
s10r8 := (s10yy1 + 91881*s10cr1) >> 16
s10g8 := (s10yy1 - 22554*s10cb1 - 46802*s10cr1) >> 16
s10b8 := (s10yy1 + 116130*s10cb1) >> 16
if s10r8 < 0 {
s10r8 = 0
} else if s10r8 > 0xff {
s10r8 = 0xff
}
if s10g8 < 0 {
s10g8 = 0
} else if s10g8 > 0xff {
s10g8 = 0xff
}
if s10b8 < 0 {
s10b8 = 0
} else if s10b8 > 0xff {
s10b8 = 0xff
}
s10ru := uint32(s10r8) * 0x101
s10gu := uint32(s10g8) * 0x101
s10bu := uint32(s10b8) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s01i := (sy1-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
s01j := (sy1-src.Rect.Min.Y)*src.CStride + ((sx0)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s01yy1 := int(src.Y[s01i])<<16 + 1<<15
s01cb1 := int(src.Cb[s01j]) - 128
s01cr1 := int(src.Cr[s01j]) - 128
s01r8 := (s01yy1 + 91881*s01cr1) >> 16
s01g8 := (s01yy1 - 22554*s01cb1 - 46802*s01cr1) >> 16
s01b8 := (s01yy1 + 116130*s01cb1) >> 16
if s01r8 < 0 {
s01r8 = 0
} else if s01r8 > 0xff {
s01r8 = 0xff
}
if s01g8 < 0 {
s01g8 = 0
} else if s01g8 > 0xff {
s01g8 = 0xff
}
if s01b8 < 0 {
s01b8 = 0
} else if s01b8 > 0xff {
s01b8 = 0xff
}
s01ru := uint32(s01r8) * 0x101
s01gu := uint32(s01g8) * 0x101
s01bu := uint32(s01b8) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s11i := (sy1-src.Rect.Min.Y)*src.YStride + (sx1 - src.Rect.Min.X)
s11j := (sy1-src.Rect.Min.Y)*src.CStride + ((sx1)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s11yy1 := int(src.Y[s11i])<<16 + 1<<15
s11cb1 := int(src.Cb[s11j]) - 128
s11cr1 := int(src.Cr[s11j]) - 128
s11r8 := (s11yy1 + 91881*s11cr1) >> 16
s11g8 := (s11yy1 - 22554*s11cb1 - 46802*s11cr1) >> 16
s11b8 := (s11yy1 + 116130*s11cb1) >> 16
if s11r8 < 0 {
s11r8 = 0
} else if s11r8 > 0xff {
s11r8 = 0xff
}
if s11g8 < 0 {
s11g8 = 0
} else if s11g8 > 0xff {
s11g8 = 0xff
}
if s11b8 < 0 {
s11b8 = 0
} else if s11b8 > 0xff {
s11b8 = 0xff
}
s11ru := uint32(s11r8) * 0x101
s11gu := uint32(s11g8) * 0x101
s11bu := uint32(s11b8) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) transform_RGBA_YCbCr420(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00i := (sy0-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
s00j := ((sy0)/2-src.Rect.Min.Y/2)*src.CStride + ((sx0)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s00yy1 := int(src.Y[s00i])<<16 + 1<<15
s00cb1 := int(src.Cb[s00j]) - 128
s00cr1 := int(src.Cr[s00j]) - 128
s00r8 := (s00yy1 + 91881*s00cr1) >> 16
s00g8 := (s00yy1 - 22554*s00cb1 - 46802*s00cr1) >> 16
s00b8 := (s00yy1 + 116130*s00cb1) >> 16
if s00r8 < 0 {
s00r8 = 0
} else if s00r8 > 0xff {
s00r8 = 0xff
}
if s00g8 < 0 {
s00g8 = 0
} else if s00g8 > 0xff {
s00g8 = 0xff
}
if s00b8 < 0 {
s00b8 = 0
} else if s00b8 > 0xff {
s00b8 = 0xff
}
s00ru := uint32(s00r8) * 0x101
s00gu := uint32(s00g8) * 0x101
s00bu := uint32(s00b8) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s10i := (sy0-src.Rect.Min.Y)*src.YStride + (sx1 - src.Rect.Min.X)
s10j := ((sy0)/2-src.Rect.Min.Y/2)*src.CStride + ((sx1)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s10yy1 := int(src.Y[s10i])<<16 + 1<<15
s10cb1 := int(src.Cb[s10j]) - 128
s10cr1 := int(src.Cr[s10j]) - 128
s10r8 := (s10yy1 + 91881*s10cr1) >> 16
s10g8 := (s10yy1 - 22554*s10cb1 - 46802*s10cr1) >> 16
s10b8 := (s10yy1 + 116130*s10cb1) >> 16
if s10r8 < 0 {
s10r8 = 0
} else if s10r8 > 0xff {
s10r8 = 0xff
}
if s10g8 < 0 {
s10g8 = 0
} else if s10g8 > 0xff {
s10g8 = 0xff
}
if s10b8 < 0 {
s10b8 = 0
} else if s10b8 > 0xff {
s10b8 = 0xff
}
s10ru := uint32(s10r8) * 0x101
s10gu := uint32(s10g8) * 0x101
s10bu := uint32(s10b8) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s01i := (sy1-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
s01j := ((sy1)/2-src.Rect.Min.Y/2)*src.CStride + ((sx0)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s01yy1 := int(src.Y[s01i])<<16 + 1<<15
s01cb1 := int(src.Cb[s01j]) - 128
s01cr1 := int(src.Cr[s01j]) - 128
s01r8 := (s01yy1 + 91881*s01cr1) >> 16
s01g8 := (s01yy1 - 22554*s01cb1 - 46802*s01cr1) >> 16
s01b8 := (s01yy1 + 116130*s01cb1) >> 16
if s01r8 < 0 {
s01r8 = 0
} else if s01r8 > 0xff {
s01r8 = 0xff
}
if s01g8 < 0 {
s01g8 = 0
} else if s01g8 > 0xff {
s01g8 = 0xff
}
if s01b8 < 0 {
s01b8 = 0
} else if s01b8 > 0xff {
s01b8 = 0xff
}
s01ru := uint32(s01r8) * 0x101
s01gu := uint32(s01g8) * 0x101
s01bu := uint32(s01b8) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s11i := (sy1-src.Rect.Min.Y)*src.YStride + (sx1 - src.Rect.Min.X)
s11j := ((sy1)/2-src.Rect.Min.Y/2)*src.CStride + ((sx1)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s11yy1 := int(src.Y[s11i])<<16 + 1<<15
s11cb1 := int(src.Cb[s11j]) - 128
s11cr1 := int(src.Cr[s11j]) - 128
s11r8 := (s11yy1 + 91881*s11cr1) >> 16
s11g8 := (s11yy1 - 22554*s11cb1 - 46802*s11cr1) >> 16
s11b8 := (s11yy1 + 116130*s11cb1) >> 16
if s11r8 < 0 {
s11r8 = 0
} else if s11r8 > 0xff {
s11r8 = 0xff
}
if s11g8 < 0 {
s11g8 = 0
} else if s11g8 > 0xff {
s11g8 = 0xff
}
if s11b8 < 0 {
s11b8 = 0
} else if s11b8 > 0xff {
s11b8 = 0xff
}
s11ru := uint32(s11r8) * 0x101
s11gu := uint32(s11g8) * 0x101
s11bu := uint32(s11b8) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) transform_RGBA_YCbCr440(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00i := (sy0-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
s00j := ((sy0)/2-src.Rect.Min.Y/2)*src.CStride + (sx0 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s00yy1 := int(src.Y[s00i])<<16 + 1<<15
s00cb1 := int(src.Cb[s00j]) - 128
s00cr1 := int(src.Cr[s00j]) - 128
s00r8 := (s00yy1 + 91881*s00cr1) >> 16
s00g8 := (s00yy1 - 22554*s00cb1 - 46802*s00cr1) >> 16
s00b8 := (s00yy1 + 116130*s00cb1) >> 16
if s00r8 < 0 {
s00r8 = 0
} else if s00r8 > 0xff {
s00r8 = 0xff
}
if s00g8 < 0 {
s00g8 = 0
} else if s00g8 > 0xff {
s00g8 = 0xff
}
if s00b8 < 0 {
s00b8 = 0
} else if s00b8 > 0xff {
s00b8 = 0xff
}
s00ru := uint32(s00r8) * 0x101
s00gu := uint32(s00g8) * 0x101
s00bu := uint32(s00b8) * 0x101
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s10i := (sy0-src.Rect.Min.Y)*src.YStride + (sx1 - src.Rect.Min.X)
s10j := ((sy0)/2-src.Rect.Min.Y/2)*src.CStride + (sx1 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s10yy1 := int(src.Y[s10i])<<16 + 1<<15
s10cb1 := int(src.Cb[s10j]) - 128
s10cr1 := int(src.Cr[s10j]) - 128
s10r8 := (s10yy1 + 91881*s10cr1) >> 16
s10g8 := (s10yy1 - 22554*s10cb1 - 46802*s10cr1) >> 16
s10b8 := (s10yy1 + 116130*s10cb1) >> 16
if s10r8 < 0 {
s10r8 = 0
} else if s10r8 > 0xff {
s10r8 = 0xff
}
if s10g8 < 0 {
s10g8 = 0
} else if s10g8 > 0xff {
s10g8 = 0xff
}
if s10b8 < 0 {
s10b8 = 0
} else if s10b8 > 0xff {
s10b8 = 0xff
}
s10ru := uint32(s10r8) * 0x101
s10gu := uint32(s10g8) * 0x101
s10bu := uint32(s10b8) * 0x101
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s01i := (sy1-src.Rect.Min.Y)*src.YStride + (sx0 - src.Rect.Min.X)
s01j := ((sy1)/2-src.Rect.Min.Y/2)*src.CStride + (sx0 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s01yy1 := int(src.Y[s01i])<<16 + 1<<15
s01cb1 := int(src.Cb[s01j]) - 128
s01cr1 := int(src.Cr[s01j]) - 128
s01r8 := (s01yy1 + 91881*s01cr1) >> 16
s01g8 := (s01yy1 - 22554*s01cb1 - 46802*s01cr1) >> 16
s01b8 := (s01yy1 + 116130*s01cb1) >> 16
if s01r8 < 0 {
s01r8 = 0
} else if s01r8 > 0xff {
s01r8 = 0xff
}
if s01g8 < 0 {
s01g8 = 0
} else if s01g8 > 0xff {
s01g8 = 0xff
}
if s01b8 < 0 {
s01b8 = 0
} else if s01b8 > 0xff {
s01b8 = 0xff
}
s01ru := uint32(s01r8) * 0x101
s01gu := uint32(s01g8) * 0x101
s01bu := uint32(s01b8) * 0x101
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s11i := (sy1-src.Rect.Min.Y)*src.YStride + (sx1 - src.Rect.Min.X)
s11j := ((sy1)/2-src.Rect.Min.Y/2)*src.CStride + (sx1 - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
s11yy1 := int(src.Y[s11i])<<16 + 1<<15
s11cb1 := int(src.Cb[s11j]) - 128
s11cr1 := int(src.Cr[s11j]) - 128
s11r8 := (s11yy1 + 91881*s11cr1) >> 16
s11g8 := (s11yy1 - 22554*s11cb1 - 46802*s11cr1) >> 16
s11b8 := (s11yy1 + 116130*s11cb1) >> 16
if s11r8 < 0 {
s11r8 = 0
} else if s11r8 > 0xff {
s11r8 = 0xff
}
if s11g8 < 0 {
s11g8 = 0
} else if s11g8 > 0xff {
s11g8 = 0xff
}
if s11b8 < 0 {
s11b8 = 0
} else if s11b8 > 0xff {
s11b8 = 0xff
}
s11ru := uint32(s11r8) * 0x101
s11gu := uint32(s11g8) * 0x101
s11bu := uint32(s11b8) * 0x101
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (ablInterpolator) transform_RGBA_Image(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point) {
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00ru, s00gu, s00bu, s00au := src.At(sx0, sy0).RGBA()
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s00a := float64(s00au)
s10ru, s10gu, s10bu, s10au := src.At(sx1, sy0).RGBA()
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10a := float64(s10au)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s10a = xFrac1*s00a + xFrac0*s10a
s01ru, s01gu, s01bu, s01au := src.At(sx0, sy1).RGBA()
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s01a := float64(s01au)
s11ru, s11gu, s11bu, s11au := src.At(sx1, sy1).RGBA()
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11a := float64(s11au)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11a = xFrac1*s01a + xFrac0*s11a
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
s11a = yFrac1*s10a + yFrac0*s11a
dst.Pix[d+0] = uint8(uint32(s11r) >> 8)
dst.Pix[d+1] = uint8(uint32(s11g) >> 8)
dst.Pix[d+2] = uint8(uint32(s11b) >> 8)
dst.Pix[d+3] = uint8(uint32(s11a) >> 8)
}
}
}
func (ablInterpolator) transform_Image_Image(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point) {
dstColorRGBA64 := &color.RGBA64{}
dstColor := color.Color(dstColorRGBA64)
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
sx -= 0.5
sx0 := int(sx)
xFrac0 := sx - float64(sx0)
xFrac1 := 1 - xFrac0
sx0 += bias.X
sx1 := sx0 + 1
if sx0 < sr.Min.X {
sx0, sx1 = sr.Min.X, sr.Min.X
xFrac0, xFrac1 = 0, 1
} else if sx1 >= sr.Max.X {
sx0, sx1 = sr.Max.X-1, sr.Max.X-1
xFrac0, xFrac1 = 1, 0
}
sy -= 0.5
sy0 := int(sy)
yFrac0 := sy - float64(sy0)
yFrac1 := 1 - yFrac0
sy0 += bias.Y
sy1 := sy0 + 1
if sy0 < sr.Min.Y {
sy0, sy1 = sr.Min.Y, sr.Min.Y
yFrac0, yFrac1 = 0, 1
} else if sy1 >= sr.Max.Y {
sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
yFrac0, yFrac1 = 1, 0
}
s00ru, s00gu, s00bu, s00au := src.At(sx0, sy0).RGBA()
s00r := float64(s00ru)
s00g := float64(s00gu)
s00b := float64(s00bu)
s00a := float64(s00au)
s10ru, s10gu, s10bu, s10au := src.At(sx1, sy0).RGBA()
s10r := float64(s10ru)
s10g := float64(s10gu)
s10b := float64(s10bu)
s10a := float64(s10au)
s10r = xFrac1*s00r + xFrac0*s10r
s10g = xFrac1*s00g + xFrac0*s10g
s10b = xFrac1*s00b + xFrac0*s10b
s10a = xFrac1*s00a + xFrac0*s10a
s01ru, s01gu, s01bu, s01au := src.At(sx0, sy1).RGBA()
s01r := float64(s01ru)
s01g := float64(s01gu)
s01b := float64(s01bu)
s01a := float64(s01au)
s11ru, s11gu, s11bu, s11au := src.At(sx1, sy1).RGBA()
s11r := float64(s11ru)
s11g := float64(s11gu)
s11b := float64(s11bu)
s11a := float64(s11au)
s11r = xFrac1*s01r + xFrac0*s11r
s11g = xFrac1*s01g + xFrac0*s11g
s11b = xFrac1*s01b + xFrac0*s11b
s11a = xFrac1*s01a + xFrac0*s11a
s11r = yFrac1*s10r + yFrac0*s11r
s11g = yFrac1*s10g + yFrac0*s11g
s11b = yFrac1*s10b + yFrac0*s11b
s11a = yFrac1*s10a + yFrac0*s11a
dstColorRGBA64.R = uint16(s11r)
dstColorRGBA64.G = uint16(s11g)
dstColorRGBA64.B = uint16(s11b)
dstColorRGBA64.A = uint16(s11a)
dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
}
}
}
func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) {
if z.dw != int32(dr.Dx()) || z.dh != int32(dr.Dy()) || z.sw != int32(sr.Dx()) || z.sh != int32(sr.Dy()) {
z.kernel.Scale(dst, dr, src, sr, opts)
return
}
// adr is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
if adr.Empty() || sr.Empty() {
return
}
if _, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) {
// TODO: get the Op from opts.
Draw(dst, dr, src, src.Bounds().Min, Src)
return
}
// Create a temporary buffer:
// scaleX distributes the source image's columns over the temporary image.
// scaleY distributes the temporary image's rows over the destination image.
// TODO: is it worth having a sync.Pool for this temporary buffer?
tmp := make([][4]float64, z.dw*z.sh)
// sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking.
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
if !sr.In(src.Bounds()) {
z.scaleX_Image(tmp, src, sr)
} else {
switch src := src.(type) {
case *image.Gray:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scaleX_Gray(tmp, src, sr)
case *image.NRGBA:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scaleX_NRGBA(tmp, src, sr)
case *image.RGBA:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scaleX_RGBA(tmp, src, sr)
case *image.YCbCr:
switch src.SubsampleRatio {
default:
z.scaleX_Image(tmp, src, sr)
case image.YCbCrSubsampleRatio444:
z.scaleX_YCbCr444(tmp, src, sr)
case image.YCbCrSubsampleRatio422:
z.scaleX_YCbCr422(tmp, src, sr)
case image.YCbCrSubsampleRatio420:
z.scaleX_YCbCr420(tmp, src, sr)
case image.YCbCrSubsampleRatio440:
z.scaleX_YCbCr440(tmp, src, sr)
}
default:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scaleX_Image(tmp, src, sr)
}
}
switch dst := dst.(type) {
case *image.RGBA:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scaleY_RGBA(dst, dr, adr, tmp)
default:
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
z.scaleY_Image(dst, dr, adr, tmp)
}
}
func (q *Kernel) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) {
dr := transformRect(s2d, &sr)
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
if adr.Empty() || sr.Empty() {
return
}
d2s := invert(s2d)
// bias is a translation of the mapping from dst co-ordinates to
// src co-ordinates such that the latter temporarily have
// non-negative X and Y co-ordinates. This allows us to write
// int(f) instead of int(math.Floor(f)), since "round to zero" and
// "round down" are equivalent when f >= 0, but the former is much
// cheaper. The X-- and Y-- are because the TransformLeaf methods
// have a "sx -= 0.5" adjustment.
bias := transformRect(&d2s, &adr).Min
bias.X--
bias.Y--
d2s[2] -= float64(bias.X)
d2s[5] -= float64(bias.Y)
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if u, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) {
// TODO: get the Op from opts.
transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, Src)
return
}
xscale := abs(d2s[0])
if s := abs(d2s[1]); xscale < s {
xscale = s
}
yscale := abs(d2s[3])
if s := abs(d2s[4]); yscale < s {
yscale = s
}
// sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking.
if !sr.In(src.Bounds()) {
q.transform_Image_Image(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
} else {
switch dst := dst.(type) {
case *image.RGBA:
switch src := src.(type) {
case *image.Gray:
q.transform_RGBA_Gray(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case *image.NRGBA:
q.transform_RGBA_NRGBA(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case *image.RGBA:
q.transform_RGBA_RGBA(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case *image.YCbCr:
switch src.SubsampleRatio {
default:
q.transform_RGBA_Image(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case image.YCbCrSubsampleRatio444:
q.transform_RGBA_YCbCr444(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case image.YCbCrSubsampleRatio422:
q.transform_RGBA_YCbCr422(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case image.YCbCrSubsampleRatio420:
q.transform_RGBA_YCbCr420(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case image.YCbCrSubsampleRatio440:
q.transform_RGBA_YCbCr440(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
}
default:
q.transform_RGBA_Image(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
}
default:
switch src := src.(type) {
default:
q.transform_Image_Image(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
}
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (z *kernelScaler) scaleX_Gray(tmp [][4]float64, src *image.Gray, sr image.Rectangle) {
t := 0
for y := int32(0); y < z.sh; y++ {
for _, s := range z.horizontal.sources {
var pr float64
for _, c := range z.horizontal.contribs[s.i:s.j] {
pi := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.Stride + (sr.Min.X + int(c.coord) - src.Rect.Min.X)
pru := uint32(src.Pix[pi]) * 0x101
pr += float64(pru) * c.weight
}
pr *= s.invTotalWeightFFFF
tmp[t] = [4]float64{
pr,
pr,
pr,
1,
}
t++
}
}
}
draw: make Scale an Interpolator method instead of a function. This means that only Kernel values have a NewScaler method, which re-uses computation when scaling multiple images of the same dst and src dimensions. The NearestNeighbor and ApproxBiLinear scalers don't get any pre-computation to re-use, so don't need a NewScaler method just to satisfy the previous Interpolator interface. As a small bonus, NN.Scale and ABL.Scale should no longer allocate on the fast paths. This change is consistent the upcoming Transformer method, so that the Interpolator interface will be type Interpolator interface { Scale(etc) Transform(etc) } instead of type Interpolator interface { NewScaler(etc) Scaler Transform(etc) } I don't have a good theory for why the "func (ablInterpolator) scale_RGBA_RGBA" benchmark is such a dramatic improvement, but at least it's in the right direction. I'm calling the other benchmark changes as noise. benchmark old ns/op new ns/op delta BenchmarkScaleLargeDownNN 3233406 3169060 -1.99% BenchmarkScaleLargeDownAB 12018178 12011348 -0.06% BenchmarkScaleLargeDownBL 1420827834 1409335695 -0.81% BenchmarkScaleLargeDownCR 2820669690 2795534035 -0.89% BenchmarkScaleDownNN 866628 869241 +0.30% BenchmarkScaleDownAB 3175963 3216041 +1.26% BenchmarkScaleDownBL 26639767 26677003 +0.14% BenchmarkScaleDownCR 51720996 51621628 -0.19% BenchmarkScaleUpNN 42758485 43258611 +1.17% BenchmarkScaleUpAB 156693813 156943367 +0.16% BenchmarkScaleUpBL 69511444 69621698 +0.16% BenchmarkScaleUpCR 124530191 124885601 +0.29% BenchmarkScaleSrcGray 8992205 9129321 +1.52% BenchmarkScaleSrcNRGBA 9807837 9894466 +0.88% BenchmarkScaleSrcRGBA 1333188 1104282 -17.17% BenchmarkScaleSrcUniform 1147788 1162488 +1.28% BenchmarkScaleSrcYCbCr 12164542 12305373 +1.16% Change-Id: I2aee6c392eb7437e843260775aed97ce145b4d47 Reviewed-on: https://go-review.googlesource.com/6556 Reviewed-by: Rob Pike <r@golang.org>
2015-03-03 06:54:53 +01:00
func (z *kernelScaler) scaleX_NRGBA(tmp [][4]float64, src *image.NRGBA, sr image.Rectangle) {
t := 0
for y := int32(0); y < z.sh; y++ {
for _, s := range z.horizontal.sources {
var pr, pg, pb, pa float64
for _, c := range z.horizontal.contribs[s.i:s.j] {
pi := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(c.coord)-src.Rect.Min.X)*4
pau := uint32(src.Pix[pi+3]) * 0x101
pru := uint32(src.Pix[pi+0]) * pau / 0xff
pgu := uint32(src.Pix[pi+1]) * pau / 0xff
pbu := uint32(src.Pix[pi+2]) * pau / 0xff
pr += float64(pru) * c.weight
pg += float64(pgu) * c.weight
pb += float64(pbu) * c.weight
pa += float64(pau) * c.weight
}
tmp[t] = [4]float64{
pr * s.invTotalWeightFFFF,
pg * s.invTotalWeightFFFF,
pb * s.invTotalWeightFFFF,
pa * s.invTotalWeightFFFF,
}
t++
}
}
}
func (z *kernelScaler) scaleX_RGBA(tmp [][4]float64, src *image.RGBA, sr image.Rectangle) {
t := 0
for y := int32(0); y < z.sh; y++ {
for _, s := range z.horizontal.sources {
var pr, pg, pb, pa float64
for _, c := range z.horizontal.contribs[s.i:s.j] {
pi := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.Stride + (sr.Min.X+int(c.coord)-src.Rect.Min.X)*4
pru := uint32(src.Pix[pi+0]) * 0x101
pgu := uint32(src.Pix[pi+1]) * 0x101
pbu := uint32(src.Pix[pi+2]) * 0x101
pau := uint32(src.Pix[pi+3]) * 0x101
pr += float64(pru) * c.weight
pg += float64(pgu) * c.weight
pb += float64(pbu) * c.weight
pa += float64(pau) * c.weight
}
tmp[t] = [4]float64{
pr * s.invTotalWeightFFFF,
pg * s.invTotalWeightFFFF,
pb * s.invTotalWeightFFFF,
pa * s.invTotalWeightFFFF,
}
t++
}
}
}
func (z *kernelScaler) scaleX_YCbCr444(tmp [][4]float64, src *image.YCbCr, sr image.Rectangle) {
t := 0
for y := int32(0); y < z.sh; y++ {
for _, s := range z.horizontal.sources {
var pr, pg, pb float64
for _, c := range z.horizontal.contribs[s.i:s.j] {
pi := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(c.coord) - src.Rect.Min.X)
pj := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.CStride + (sr.Min.X + int(c.coord) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pru := uint32(pr8) * 0x101
pgu := uint32(pg8) * 0x101
pbu := uint32(pb8) * 0x101
pr += float64(pru) * c.weight
pg += float64(pgu) * c.weight
pb += float64(pbu) * c.weight
}
tmp[t] = [4]float64{
pr * s.invTotalWeightFFFF,
pg * s.invTotalWeightFFFF,
pb * s.invTotalWeightFFFF,
1,
}
t++
}
}
}
func (z *kernelScaler) scaleX_YCbCr422(tmp [][4]float64, src *image.YCbCr, sr image.Rectangle) {
t := 0
for y := int32(0); y < z.sh; y++ {
for _, s := range z.horizontal.sources {
var pr, pg, pb float64
for _, c := range z.horizontal.contribs[s.i:s.j] {
pi := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(c.coord) - src.Rect.Min.X)
pj := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.CStride + ((sr.Min.X+int(c.coord))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pru := uint32(pr8) * 0x101
pgu := uint32(pg8) * 0x101
pbu := uint32(pb8) * 0x101
pr += float64(pru) * c.weight
pg += float64(pgu) * c.weight
pb += float64(pbu) * c.weight
}
tmp[t] = [4]float64{
pr * s.invTotalWeightFFFF,
pg * s.invTotalWeightFFFF,
pb * s.invTotalWeightFFFF,
1,
}
t++
}
}
}
func (z *kernelScaler) scaleX_YCbCr420(tmp [][4]float64, src *image.YCbCr, sr image.Rectangle) {
t := 0
for y := int32(0); y < z.sh; y++ {
for _, s := range z.horizontal.sources {
var pr, pg, pb float64
for _, c := range z.horizontal.contribs[s.i:s.j] {
pi := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(c.coord) - src.Rect.Min.X)
pj := ((sr.Min.Y+int(y))/2-src.Rect.Min.Y/2)*src.CStride + ((sr.Min.X+int(c.coord))/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pru := uint32(pr8) * 0x101
pgu := uint32(pg8) * 0x101
pbu := uint32(pb8) * 0x101
pr += float64(pru) * c.weight
pg += float64(pgu) * c.weight
pb += float64(pbu) * c.weight
}
tmp[t] = [4]float64{
pr * s.invTotalWeightFFFF,
pg * s.invTotalWeightFFFF,
pb * s.invTotalWeightFFFF,
1,
}
t++
}
}
}
func (z *kernelScaler) scaleX_YCbCr440(tmp [][4]float64, src *image.YCbCr, sr image.Rectangle) {
t := 0
for y := int32(0); y < z.sh; y++ {
for _, s := range z.horizontal.sources {
var pr, pg, pb float64
for _, c := range z.horizontal.contribs[s.i:s.j] {
pi := (sr.Min.Y+int(y)-src.Rect.Min.Y)*src.YStride + (sr.Min.X + int(c.coord) - src.Rect.Min.X)
pj := ((sr.Min.Y+int(y))/2-src.Rect.Min.Y/2)*src.CStride + (sr.Min.X + int(c.coord) - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pru := uint32(pr8) * 0x101
pgu := uint32(pg8) * 0x101
pbu := uint32(pb8) * 0x101
pr += float64(pru) * c.weight
pg += float64(pgu) * c.weight
pb += float64(pbu) * c.weight
}
tmp[t] = [4]float64{
pr * s.invTotalWeightFFFF,
pg * s.invTotalWeightFFFF,
pb * s.invTotalWeightFFFF,
1,
}
t++
}
}
}
func (z *kernelScaler) scaleX_Image(tmp [][4]float64, src image.Image, sr image.Rectangle) {
t := 0
for y := int32(0); y < z.sh; y++ {
for _, s := range z.horizontal.sources {
var pr, pg, pb, pa float64
for _, c := range z.horizontal.contribs[s.i:s.j] {
pru, pgu, pbu, pau := src.At(sr.Min.X+int(c.coord), sr.Min.Y+int(y)).RGBA()
pr += float64(pru) * c.weight
pg += float64(pgu) * c.weight
pb += float64(pbu) * c.weight
pa += float64(pau) * c.weight
}
tmp[t] = [4]float64{
pr * s.invTotalWeightFFFF,
pg * s.invTotalWeightFFFF,
pb * s.invTotalWeightFFFF,
pa * s.invTotalWeightFFFF,
}
t++
}
}
}
func (z *kernelScaler) scaleY_RGBA(dst *image.RGBA, dr, adr image.Rectangle, tmp [][4]float64) {
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
d := (dr.Min.Y+adr.Min.Y-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+int(dx)-dst.Rect.Min.X)*4
for _, s := range z.vertical.sources[adr.Min.Y:adr.Max.Y] {
var pr, pg, pb, pa float64
for _, c := range z.vertical.contribs[s.i:s.j] {
p := &tmp[c.coord*z.dw+dx]
pr += p[0] * c.weight
pg += p[1] * c.weight
pb += p[2] * c.weight
pa += p[3] * c.weight
}
dst.Pix[d+0] = uint8(ftou(pr*s.invTotalWeight) >> 8)
dst.Pix[d+1] = uint8(ftou(pg*s.invTotalWeight) >> 8)
dst.Pix[d+2] = uint8(ftou(pb*s.invTotalWeight) >> 8)
dst.Pix[d+3] = uint8(ftou(pa*s.invTotalWeight) >> 8)
d += dst.Stride
}
}
}
func (z *kernelScaler) scaleY_Image(dst Image, dr, adr image.Rectangle, tmp [][4]float64) {
dstColorRGBA64 := &color.RGBA64{}
dstColor := color.Color(dstColorRGBA64)
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
for dy, s := range z.vertical.sources[adr.Min.Y:adr.Max.Y] {
var pr, pg, pb, pa float64
for _, c := range z.vertical.contribs[s.i:s.j] {
p := &tmp[c.coord*z.dw+dx]
pr += p[0] * c.weight
pg += p[1] * c.weight
pb += p[2] * c.weight
pa += p[3] * c.weight
}
dstColorRGBA64.R = ftou(pr * s.invTotalWeight)
dstColorRGBA64.G = ftou(pg * s.invTotalWeight)
dstColorRGBA64.B = ftou(pb * s.invTotalWeight)
dstColorRGBA64.A = ftou(pa * s.invTotalWeight)
dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(adr.Min.Y+dy), dstColor)
}
}
}
func (q *Kernel) transform_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.Gray, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pi := (ky-src.Rect.Min.Y)*src.Stride + (kx - src.Rect.Min.X)
pru := uint32(src.Pix[pi]) * 0x101
pr += float64(pru) * w
}
}
}
}
out := uint8(fffftou(pr) >> 8)
dst.Pix[d+0] = out
dst.Pix[d+1] = out
dst.Pix[d+2] = out
dst.Pix[d+3] = 0xff
}
}
}
func (q *Kernel) transform_RGBA_NRGBA(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.NRGBA, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr, pg, pb, pa float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pi := (ky-src.Rect.Min.Y)*src.Stride + (kx-src.Rect.Min.X)*4
pau := uint32(src.Pix[pi+3]) * 0x101
pru := uint32(src.Pix[pi+0]) * pau / 0xff
pgu := uint32(src.Pix[pi+1]) * pau / 0xff
pbu := uint32(src.Pix[pi+2]) * pau / 0xff
pr += float64(pru) * w
pg += float64(pgu) * w
pb += float64(pbu) * w
pa += float64(pau) * w
}
}
}
}
dst.Pix[d+0] = uint8(fffftou(pr) >> 8)
dst.Pix[d+1] = uint8(fffftou(pg) >> 8)
dst.Pix[d+2] = uint8(fffftou(pb) >> 8)
dst.Pix[d+3] = uint8(fffftou(pa) >> 8)
}
}
}
func (q *Kernel) transform_RGBA_RGBA(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.RGBA, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr, pg, pb, pa float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pi := (ky-src.Rect.Min.Y)*src.Stride + (kx-src.Rect.Min.X)*4
pru := uint32(src.Pix[pi+0]) * 0x101
pgu := uint32(src.Pix[pi+1]) * 0x101
pbu := uint32(src.Pix[pi+2]) * 0x101
pau := uint32(src.Pix[pi+3]) * 0x101
pr += float64(pru) * w
pg += float64(pgu) * w
pb += float64(pbu) * w
pa += float64(pau) * w
}
}
}
}
dst.Pix[d+0] = uint8(fffftou(pr) >> 8)
dst.Pix[d+1] = uint8(fffftou(pg) >> 8)
dst.Pix[d+2] = uint8(fffftou(pb) >> 8)
dst.Pix[d+3] = uint8(fffftou(pa) >> 8)
}
}
}
func (q *Kernel) transform_RGBA_YCbCr444(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr, pg, pb float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pi := (ky-src.Rect.Min.Y)*src.YStride + (kx - src.Rect.Min.X)
pj := (ky-src.Rect.Min.Y)*src.CStride + (kx - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pru := uint32(pr8) * 0x101
pgu := uint32(pg8) * 0x101
pbu := uint32(pb8) * 0x101
pr += float64(pru) * w
pg += float64(pgu) * w
pb += float64(pbu) * w
}
}
}
}
dst.Pix[d+0] = uint8(fffftou(pr) >> 8)
dst.Pix[d+1] = uint8(fffftou(pg) >> 8)
dst.Pix[d+2] = uint8(fffftou(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (q *Kernel) transform_RGBA_YCbCr422(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr, pg, pb float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pi := (ky-src.Rect.Min.Y)*src.YStride + (kx - src.Rect.Min.X)
pj := (ky-src.Rect.Min.Y)*src.CStride + ((kx)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pru := uint32(pr8) * 0x101
pgu := uint32(pg8) * 0x101
pbu := uint32(pb8) * 0x101
pr += float64(pru) * w
pg += float64(pgu) * w
pb += float64(pbu) * w
}
}
}
}
dst.Pix[d+0] = uint8(fffftou(pr) >> 8)
dst.Pix[d+1] = uint8(fffftou(pg) >> 8)
dst.Pix[d+2] = uint8(fffftou(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (q *Kernel) transform_RGBA_YCbCr420(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr, pg, pb float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pi := (ky-src.Rect.Min.Y)*src.YStride + (kx - src.Rect.Min.X)
pj := ((ky)/2-src.Rect.Min.Y/2)*src.CStride + ((kx)/2 - src.Rect.Min.X/2)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pru := uint32(pr8) * 0x101
pgu := uint32(pg8) * 0x101
pbu := uint32(pb8) * 0x101
pr += float64(pru) * w
pg += float64(pgu) * w
pb += float64(pbu) * w
}
}
}
}
dst.Pix[d+0] = uint8(fffftou(pr) >> 8)
dst.Pix[d+1] = uint8(fffftou(pg) >> 8)
dst.Pix[d+2] = uint8(fffftou(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (q *Kernel) transform_RGBA_YCbCr440(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.YCbCr, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr, pg, pb float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pi := (ky-src.Rect.Min.Y)*src.YStride + (kx - src.Rect.Min.X)
pj := ((ky)/2-src.Rect.Min.Y/2)*src.CStride + (kx - src.Rect.Min.X)
// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
pyy1 := int(src.Y[pi])<<16 + 1<<15
pcb1 := int(src.Cb[pj]) - 128
pcr1 := int(src.Cr[pj]) - 128
pr8 := (pyy1 + 91881*pcr1) >> 16
pg8 := (pyy1 - 22554*pcb1 - 46802*pcr1) >> 16
pb8 := (pyy1 + 116130*pcb1) >> 16
if pr8 < 0 {
pr8 = 0
} else if pr8 > 0xff {
pr8 = 0xff
}
if pg8 < 0 {
pg8 = 0
} else if pg8 > 0xff {
pg8 = 0xff
}
if pb8 < 0 {
pb8 = 0
} else if pb8 > 0xff {
pb8 = 0xff
}
pru := uint32(pr8) * 0x101
pgu := uint32(pg8) * 0x101
pbu := uint32(pb8) * 0x101
pr += float64(pru) * w
pg += float64(pgu) * w
pb += float64(pbu) * w
}
}
}
}
dst.Pix[d+0] = uint8(fffftou(pr) >> 8)
dst.Pix[d+1] = uint8(fffftou(pg) >> 8)
dst.Pix[d+2] = uint8(fffftou(pb) >> 8)
dst.Pix[d+3] = 0xff
}
}
}
func (q *Kernel) transform_RGBA_Image(dst *image.RGBA, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
d := (dr.Min.Y+int(dy)-dst.Rect.Min.Y)*dst.Stride + (dr.Min.X+adr.Min.X-dst.Rect.Min.X)*4
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr, pg, pb, pa float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pru, pgu, pbu, pau := src.At(kx, ky).RGBA()
pr += float64(pru) * w
pg += float64(pgu) * w
pb += float64(pbu) * w
pa += float64(pau) * w
}
}
}
}
dst.Pix[d+0] = uint8(fffftou(pr) >> 8)
dst.Pix[d+1] = uint8(fffftou(pg) >> 8)
dst.Pix[d+2] = uint8(fffftou(pb) >> 8)
dst.Pix[d+3] = uint8(fffftou(pa) >> 8)
}
}
}
func (q *Kernel) transform_Image_Image(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point, xscale, yscale float64) {
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
xHalfWidth, xKernelArgScale := q.Support, 1.0
if xscale > 1 {
xHalfWidth *= xscale
xKernelArgScale = 1 / xscale
}
yHalfWidth, yKernelArgScale := q.Support, 1.0
if yscale > 1 {
yHalfWidth *= yscale
yKernelArgScale = 1 / yscale
}
xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
dstColorRGBA64 := &color.RGBA64{}
dstColor := color.Color(dstColorRGBA64)
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
dyf := float64(dr.Min.Y+int(dy)) + 0.5
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
dxf := float64(dr.Min.X+int(dx)) + 0.5
sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2]
sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5]
if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
continue
}
// TODO: adjust the bias so that we can use int(f) instead
// of math.Floor(f) and math.Ceil(f).
sx += float64(bias.X)
sx -= 0.5
ix := int(math.Floor(sx - xHalfWidth))
if ix < sr.Min.X {
ix = sr.Min.X
}
jx := int(math.Ceil(sx + xHalfWidth))
if jx > sr.Max.X {
jx = sr.Max.X
}
totalXWeight := 0.0
for kx := ix; kx < jx; kx++ {
xWeight := 0.0
if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
xWeight = q.At(t)
}
xWeights[kx-ix] = xWeight
totalXWeight += xWeight
}
for x := range xWeights[:jx-ix] {
xWeights[x] /= totalXWeight
}
sy += float64(bias.Y)
sy -= 0.5
iy := int(math.Floor(sy - yHalfWidth))
if iy < sr.Min.Y {
iy = sr.Min.Y
}
jy := int(math.Ceil(sy + yHalfWidth))
if jy > sr.Max.Y {
jy = sr.Max.Y
}
totalYWeight := 0.0
for ky := iy; ky < jy; ky++ {
yWeight := 0.0
if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
yWeight = q.At(t)
}
yWeights[ky-iy] = yWeight
totalYWeight += yWeight
}
for y := range yWeights[:jy-iy] {
yWeights[y] /= totalYWeight
}
var pr, pg, pb, pa float64
for ky := iy; ky < jy; ky++ {
if yWeight := yWeights[ky-iy]; yWeight != 0 {
for kx := ix; kx < jx; kx++ {
if w := xWeights[kx-ix] * yWeight; w != 0 {
pru, pgu, pbu, pau := src.At(kx, ky).RGBA()
pr += float64(pru) * w
pg += float64(pgu) * w
pb += float64(pbu) * w
pa += float64(pau) * w
}
}
}
}
dstColorRGBA64.R = fffftou(pr)
dstColorRGBA64.G = fffftou(pg)
dstColorRGBA64.B = fffftou(pb)
dstColorRGBA64.A = fffftou(pa)
dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
}
}
}