2012-09-21 18:02:25 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
|
|
with or without fee is hereby granted, provided that the above copyright notice
|
|
|
|
and this permission notice appear in all copies.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
|
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
|
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
|
|
THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package resize
|
|
|
|
|
|
|
|
import (
|
|
|
|
"image"
|
|
|
|
"image/color"
|
|
|
|
)
|
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
// Keep value in [0,255] range.
|
|
|
|
func clampUint8(in int32) uint8 {
|
|
|
|
if in < 0 {
|
|
|
|
return 0
|
2012-12-10 18:27:21 +00:00
|
|
|
}
|
2014-07-19 11:19:31 +00:00
|
|
|
if in > 255 {
|
|
|
|
return 255
|
|
|
|
}
|
|
|
|
return uint8(in)
|
2012-12-10 18:27:21 +00:00
|
|
|
}
|
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
// Keep value in [0,65535] range.
|
|
|
|
func clampUint16(in int64) uint16 {
|
|
|
|
if in < 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
if in > 65535 {
|
|
|
|
return 65535
|
|
|
|
}
|
|
|
|
return uint16(in)
|
2012-12-10 18:27:21 +00:00
|
|
|
}
|
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
func resizeGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []int32, filterLength int) {
|
|
|
|
oldBounds := in.Bounds()
|
|
|
|
newBounds := out.Bounds()
|
2012-12-11 18:57:34 +00:00
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
|
|
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
|
|
interpX := scale*(float64(y)+0.5) + float64(oldBounds.Min.X)
|
|
|
|
start := int(interpX) - filterLength/2 + 1
|
2012-12-11 18:57:34 +00:00
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
var rgba [4]int64
|
|
|
|
var sum int64
|
|
|
|
for i := 0; i < filterLength; i++ {
|
|
|
|
xx := start + i
|
|
|
|
if xx < oldBounds.Min.X {
|
|
|
|
xx = oldBounds.Min.X
|
|
|
|
} else if xx >= oldBounds.Max.X {
|
|
|
|
xx = oldBounds.Max.X - 1
|
|
|
|
}
|
2012-09-21 18:02:25 +00:00
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
coeff := coeffs[(y-newBounds.Min.Y)*filterLength+i]
|
|
|
|
r, g, b, a := in.At(xx, x).RGBA()
|
|
|
|
rgba[0] += int64(coeff) * int64(r)
|
|
|
|
rgba[1] += int64(coeff) * int64(g)
|
|
|
|
rgba[2] += int64(coeff) * int64(b)
|
|
|
|
rgba[3] += int64(coeff) * int64(a)
|
|
|
|
sum += int64(coeff)
|
|
|
|
}
|
2012-09-21 18:02:25 +00:00
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
|
|
|
value := clampUint16(rgba[0] / sum)
|
|
|
|
out.Pix[offset+0] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+1] = uint8(value)
|
|
|
|
value = clampUint16(rgba[1] / sum)
|
|
|
|
out.Pix[offset+2] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+3] = uint8(value)
|
|
|
|
value = clampUint16(rgba[2] / sum)
|
|
|
|
out.Pix[offset+4] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+5] = uint8(value)
|
|
|
|
value = clampUint16(rgba[3] / sum)
|
|
|
|
out.Pix[offset+6] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+7] = uint8(value)
|
|
|
|
}
|
|
|
|
}
|
2012-09-21 18:02:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16, filterLength int) {
|
|
|
|
oldBounds := in.Bounds()
|
|
|
|
newBounds := out.Bounds()
|
2012-09-21 18:02:25 +00:00
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
|
|
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:]
|
|
|
|
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
|
|
interpX := scale*(float64(y)+0.5) + float64(oldBounds.Min.X)
|
|
|
|
start := int(interpX) - filterLength/2 + 1
|
2012-09-21 18:02:25 +00:00
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
var rgba [4]int32
|
|
|
|
var sum int32
|
|
|
|
for i := 0; i < filterLength; i++ {
|
|
|
|
xx := start + i
|
|
|
|
if xx < oldBounds.Min.X {
|
|
|
|
xx = oldBounds.Min.X
|
|
|
|
} else if xx >= oldBounds.Max.X {
|
|
|
|
xx = oldBounds.Max.X - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
coeff := coeffs[(y-newBounds.Min.Y)*filterLength+i]
|
|
|
|
offset := (xx - oldBounds.Min.X) * 4
|
|
|
|
rgba[0] += int32(coeff) * int32(row[offset+0])
|
|
|
|
rgba[1] += int32(coeff) * int32(row[offset+1])
|
|
|
|
rgba[2] += int32(coeff) * int32(row[offset+2])
|
|
|
|
rgba[3] += int32(coeff) * int32(row[offset+3])
|
|
|
|
sum += int32(coeff)
|
|
|
|
}
|
2012-09-21 18:02:25 +00:00
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
|
|
|
|
out.Pix[offset+0] = clampUint8(rgba[0] / sum)
|
|
|
|
out.Pix[offset+1] = clampUint8(rgba[1] / sum)
|
|
|
|
out.Pix[offset+2] = clampUint8(rgba[2] / sum)
|
|
|
|
out.Pix[offset+3] = clampUint8(rgba[3] / sum)
|
|
|
|
}
|
|
|
|
}
|
2012-09-21 18:02:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []int32, filterLength int) {
|
|
|
|
oldBounds := in.Bounds()
|
|
|
|
newBounds := out.Bounds()
|
|
|
|
|
|
|
|
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
|
|
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:]
|
|
|
|
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
|
|
interpX := scale*(float64(y)+0.5) + float64(oldBounds.Min.X)
|
|
|
|
start := int(interpX) - filterLength/2 + 1
|
|
|
|
|
|
|
|
var rgba [4]int64
|
|
|
|
var sum int64
|
|
|
|
for i := 0; i < filterLength; i++ {
|
|
|
|
xx := start + i
|
|
|
|
if xx < oldBounds.Min.X {
|
|
|
|
xx = oldBounds.Min.X
|
|
|
|
} else if xx >= oldBounds.Max.X {
|
|
|
|
xx = oldBounds.Max.X - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
coeff := coeffs[(y-newBounds.Min.Y)*filterLength+i]
|
|
|
|
offset := (xx - oldBounds.Min.X) * 8
|
|
|
|
rgba[0] += int64(coeff) * int64(uint16(row[offset+0])<<8|uint16(row[offset+1]))
|
|
|
|
rgba[1] += int64(coeff) * int64(uint16(row[offset+2])<<8|uint16(row[offset+3]))
|
|
|
|
rgba[2] += int64(coeff) * int64(uint16(row[offset+4])<<8|uint16(row[offset+5]))
|
|
|
|
rgba[3] += int64(coeff) * int64(uint16(row[offset+6])<<8|uint16(row[offset+7]))
|
|
|
|
sum += int64(coeff)
|
|
|
|
}
|
|
|
|
|
|
|
|
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
|
|
|
value := clampUint16(rgba[0] / sum)
|
|
|
|
out.Pix[offset+0] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+1] = uint8(value)
|
|
|
|
value = clampUint16(rgba[1] / sum)
|
|
|
|
out.Pix[offset+2] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+3] = uint8(value)
|
|
|
|
value = clampUint16(rgba[2] / sum)
|
|
|
|
out.Pix[offset+4] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+5] = uint8(value)
|
|
|
|
value = clampUint16(rgba[3] / sum)
|
|
|
|
out.Pix[offset+6] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+7] = uint8(value)
|
|
|
|
}
|
|
|
|
}
|
2012-09-21 18:02:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, filterLength int) {
|
|
|
|
oldBounds := in.Bounds()
|
|
|
|
newBounds := out.Bounds()
|
|
|
|
|
|
|
|
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
|
|
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:]
|
|
|
|
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
|
|
interpX := scale*(float64(y)+0.5) + float64(oldBounds.Min.X)
|
|
|
|
start := int(interpX) - filterLength/2 + 1
|
|
|
|
|
|
|
|
var gray int32
|
|
|
|
var sum int32
|
|
|
|
for i := 0; i < filterLength; i++ {
|
|
|
|
xx := start + i
|
|
|
|
if xx < oldBounds.Min.X {
|
|
|
|
xx = oldBounds.Min.X
|
|
|
|
} else if xx >= oldBounds.Max.X {
|
|
|
|
xx = oldBounds.Max.X - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
coeff := coeffs[(y-newBounds.Min.Y)*filterLength+i]
|
|
|
|
offset := (xx - oldBounds.Min.X)
|
|
|
|
gray += int32(coeff) * int32(row[offset])
|
|
|
|
sum += int32(coeff)
|
|
|
|
}
|
|
|
|
|
|
|
|
offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
|
|
|
|
out.Pix[offset] = clampUint8(gray / sum)
|
|
|
|
}
|
|
|
|
}
|
2012-09-21 18:02:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, filterLength int) {
|
|
|
|
oldBounds := in.Bounds()
|
|
|
|
newBounds := out.Bounds()
|
|
|
|
|
|
|
|
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
|
|
row := in.Pix[(x-oldBounds.Min.Y)*in.Stride:]
|
|
|
|
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
|
|
interpX := scale*(float64(y)+0.5) + float64(oldBounds.Min.X)
|
|
|
|
start := int(interpX) - filterLength/2 + 1
|
|
|
|
|
|
|
|
var gray int64
|
|
|
|
var sum int64
|
|
|
|
for i := 0; i < filterLength; i++ {
|
|
|
|
xx := start + i
|
|
|
|
if xx < oldBounds.Min.X {
|
|
|
|
xx = oldBounds.Min.X
|
|
|
|
} else if xx >= oldBounds.Max.X {
|
|
|
|
xx = oldBounds.Max.X - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
coeff := coeffs[(y-newBounds.Min.Y)*filterLength+i]
|
|
|
|
offset := (xx - oldBounds.Min.X) * 2
|
|
|
|
gray += int64(coeff) * int64(uint16(row[offset+0])<<8|uint16(row[offset+1]))
|
|
|
|
sum += int64(coeff)
|
|
|
|
}
|
|
|
|
|
|
|
|
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
|
|
|
|
value := clampUint16(gray / sum)
|
|
|
|
out.Pix[offset+0] = uint8(value >> 8)
|
|
|
|
out.Pix[offset+1] = uint8(value)
|
|
|
|
}
|
|
|
|
}
|
2012-09-21 18:02:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-19 11:19:31 +00:00
|
|
|
func convertYCbCrToRGBA(in *image.YCbCr) *image.RGBA {
|
|
|
|
out := image.NewRGBA(in.Bounds())
|
|
|
|
for y := 0; y < out.Bounds().Dy(); y++ {
|
|
|
|
for x := 0; x < out.Bounds().Dx(); x++ {
|
|
|
|
p := out.Pix[y*out.Stride+4*x:]
|
|
|
|
yi := in.YOffset(x, y)
|
|
|
|
ci := in.COffset(x, y)
|
|
|
|
r, g, b := color.YCbCrToRGB(in.Y[yi], in.Cb[ci], in.Cr[ci])
|
|
|
|
p[0] = r
|
|
|
|
p[1] = g
|
|
|
|
p[2] = b
|
|
|
|
p[3] = 0xff
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out
|
2012-09-21 18:02:25 +00:00
|
|
|
}
|