mirror of https://github.com/status-im/resize.git
229 lines
6.2 KiB
Go
229 lines
6.2 KiB
Go
|
package resize
|
||
|
|
||
|
import "image"
|
||
|
|
||
|
func floatToUint8(x float32) uint8 {
|
||
|
// Nearest-neighbor values are always
|
||
|
// positive no need to check lower-bound.
|
||
|
if x > 0xfe {
|
||
|
return 0xff
|
||
|
}
|
||
|
return uint8(x)
|
||
|
}
|
||
|
|
||
|
func floatToUint16(x float32) uint16 {
|
||
|
if x > 0xfffe {
|
||
|
return 0xffff
|
||
|
}
|
||
|
return uint16(x)
|
||
|
}
|
||
|
|
||
|
func nearestGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
|
||
|
oldBounds := in.Bounds()
|
||
|
newBounds := out.Bounds()
|
||
|
|
||
|
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
||
|
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
||
|
var rgba [4]float32
|
||
|
var sum float32
|
||
|
start := offset[y]
|
||
|
ci := (y - newBounds.Min.Y) * filterLength
|
||
|
for i := 0; i < filterLength; i++ {
|
||
|
if coeffs[ci+i] {
|
||
|
xi := start + i
|
||
|
switch {
|
||
|
case uint(xi) < uint(oldBounds.Max.X):
|
||
|
break
|
||
|
case xi >= oldBounds.Max.X:
|
||
|
xi = oldBounds.Min.X
|
||
|
default:
|
||
|
xi = oldBounds.Max.X - 1
|
||
|
}
|
||
|
r, g, b, a := in.At(xi, x).RGBA()
|
||
|
rgba[0] += float32(r)
|
||
|
rgba[1] += float32(g)
|
||
|
rgba[2] += float32(b)
|
||
|
rgba[3] += float32(a)
|
||
|
sum++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
||
|
value := floatToUint16(rgba[0] / sum)
|
||
|
out.Pix[offset+0] = uint8(value >> 8)
|
||
|
out.Pix[offset+1] = uint8(value)
|
||
|
value = floatToUint16(rgba[1] / sum)
|
||
|
out.Pix[offset+2] = uint8(value >> 8)
|
||
|
out.Pix[offset+3] = uint8(value)
|
||
|
value = floatToUint16(rgba[2] / sum)
|
||
|
out.Pix[offset+4] = uint8(value >> 8)
|
||
|
out.Pix[offset+5] = uint8(value)
|
||
|
value = floatToUint16(rgba[3] / sum)
|
||
|
out.Pix[offset+6] = uint8(value >> 8)
|
||
|
out.Pix[offset+7] = uint8(value)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func nearestRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
|
||
|
oldBounds := in.Bounds()
|
||
|
newBounds := out.Bounds()
|
||
|
minX := oldBounds.Min.X * 4
|
||
|
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 4
|
||
|
|
||
|
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++ {
|
||
|
var rgba [4]float32
|
||
|
var sum float32
|
||
|
start := offset[y]
|
||
|
ci := (y - newBounds.Min.Y) * filterLength
|
||
|
for i := 0; i < filterLength; i++ {
|
||
|
if coeffs[ci+i] {
|
||
|
xi := start + i
|
||
|
switch {
|
||
|
case uint(xi) < uint(oldBounds.Max.X):
|
||
|
xi *= 4
|
||
|
case xi >= oldBounds.Max.X:
|
||
|
xi = maxX
|
||
|
default:
|
||
|
xi = minX
|
||
|
}
|
||
|
rgba[0] += float32(row[xi+0])
|
||
|
rgba[1] += float32(row[xi+1])
|
||
|
rgba[2] += float32(row[xi+2])
|
||
|
rgba[3] += float32(row[xi+3])
|
||
|
sum++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
|
||
|
out.Pix[xo+0] = floatToUint8(rgba[0] / sum)
|
||
|
out.Pix[xo+1] = floatToUint8(rgba[1] / sum)
|
||
|
out.Pix[xo+2] = floatToUint8(rgba[2] / sum)
|
||
|
out.Pix[xo+3] = floatToUint8(rgba[3] / sum)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func nearestRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
|
||
|
oldBounds := in.Bounds()
|
||
|
newBounds := out.Bounds()
|
||
|
minX := oldBounds.Min.X * 8
|
||
|
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 8
|
||
|
|
||
|
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++ {
|
||
|
var rgba [4]float32
|
||
|
var sum float32
|
||
|
start := offset[y]
|
||
|
ci := (y - newBounds.Min.Y) * filterLength
|
||
|
for i := 0; i < filterLength; i++ {
|
||
|
if coeffs[ci+i] {
|
||
|
xi := start + i
|
||
|
switch {
|
||
|
case uint(xi) < uint(oldBounds.Max.X):
|
||
|
xi *= 8
|
||
|
case xi >= oldBounds.Max.X:
|
||
|
xi = maxX
|
||
|
default:
|
||
|
xi = minX
|
||
|
}
|
||
|
rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
|
||
|
rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
|
||
|
rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
|
||
|
rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
|
||
|
sum++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
||
|
value := floatToUint16(rgba[0] / sum)
|
||
|
out.Pix[xo+0] = uint8(value >> 8)
|
||
|
out.Pix[xo+1] = uint8(value)
|
||
|
value = floatToUint16(rgba[1] / sum)
|
||
|
out.Pix[xo+2] = uint8(value >> 8)
|
||
|
out.Pix[xo+3] = uint8(value)
|
||
|
value = floatToUint16(rgba[2] / sum)
|
||
|
out.Pix[xo+4] = uint8(value >> 8)
|
||
|
out.Pix[xo+5] = uint8(value)
|
||
|
value = floatToUint16(rgba[3] / sum)
|
||
|
out.Pix[xo+6] = uint8(value >> 8)
|
||
|
out.Pix[xo+7] = uint8(value)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool, offset []int, filterLength int) {
|
||
|
oldBounds := in.Bounds()
|
||
|
newBounds := out.Bounds()
|
||
|
minX := oldBounds.Min.X
|
||
|
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1)
|
||
|
|
||
|
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++ {
|
||
|
var gray float32
|
||
|
var sum float32
|
||
|
start := offset[y]
|
||
|
ci := (y - newBounds.Min.Y) * filterLength
|
||
|
for i := 0; i < filterLength; i++ {
|
||
|
if coeffs[ci+i] {
|
||
|
xi := start + i
|
||
|
switch {
|
||
|
case uint(xi) < uint(oldBounds.Max.X):
|
||
|
break
|
||
|
case xi >= oldBounds.Max.X:
|
||
|
xi = maxX
|
||
|
default:
|
||
|
xi = minX
|
||
|
}
|
||
|
gray += float32(row[xi])
|
||
|
sum++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
|
||
|
out.Pix[offset] = floatToUint8(gray / sum)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func nearestGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []bool, offset []int, filterLength int) {
|
||
|
oldBounds := in.Bounds()
|
||
|
newBounds := out.Bounds()
|
||
|
minX := oldBounds.Min.X * 2
|
||
|
maxX := (oldBounds.Max.X - oldBounds.Min.X - 1) * 2
|
||
|
|
||
|
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++ {
|
||
|
var gray float32
|
||
|
var sum float32
|
||
|
start := offset[y]
|
||
|
ci := (y - newBounds.Min.Y) * filterLength
|
||
|
for i := 0; i < filterLength; i++ {
|
||
|
if coeffs[ci+i] {
|
||
|
xi := start + i
|
||
|
switch {
|
||
|
case uint(xi) < uint(oldBounds.Max.X):
|
||
|
xi *= 2
|
||
|
case xi >= oldBounds.Max.X:
|
||
|
xi = maxX
|
||
|
default:
|
||
|
xi = minX
|
||
|
}
|
||
|
gray += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
|
||
|
sum++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
|
||
|
value := floatToUint16(gray / sum)
|
||
|
out.Pix[offset+0] = uint8(value >> 8)
|
||
|
out.Pix[offset+1] = uint8(value)
|
||
|
}
|
||
|
}
|
||
|
}
|