You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							318 lines
						
					
					
						
							8.7 KiB
						
					
					
				
			
		
		
	
	
							318 lines
						
					
					
						
							8.7 KiB
						
					
					
				| /*
 | |
| Copyright (c) 2014, Charlie Vieth <charlie.vieth@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"
 | |
| 
 | |
| 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) {
 | |
| 	newBounds := out.Bounds()
 | |
| 	maxX := in.Bounds().Dx() - 1
 | |
| 
 | |
| 	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 * filterLength
 | |
| 			for i := 0; i < filterLength; i++ {
 | |
| 				if coeffs[ci+i] {
 | |
| 					xi := start + i
 | |
| 					switch {
 | |
| 					case xi < 0:
 | |
| 						xi = 0
 | |
| 					case xi >= maxX:
 | |
| 						xi = maxX
 | |
| 					}
 | |
| 					r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).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) {
 | |
| 	newBounds := out.Bounds()
 | |
| 	maxX := in.Bounds().Dx() - 1
 | |
| 
 | |
| 	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
 | |
| 		row := in.Pix[x*in.Stride:]
 | |
| 		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
 | |
| 			var rgba [4]float32
 | |
| 			var sum float32
 | |
| 			start := offset[y]
 | |
| 			ci := y * filterLength
 | |
| 			for i := 0; i < filterLength; i++ {
 | |
| 				if coeffs[ci+i] {
 | |
| 					xi := start + i
 | |
| 					switch {
 | |
| 					case uint(xi) < uint(maxX):
 | |
| 						xi *= 4
 | |
| 					case xi >= maxX:
 | |
| 						xi = 4 * maxX
 | |
| 					default:
 | |
| 						xi = 0
 | |
| 					}
 | |
| 					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 nearestNRGBA(in *image.NRGBA, out *image.NRGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
 | |
| 	newBounds := out.Bounds()
 | |
| 	maxX := in.Bounds().Dx() - 1
 | |
| 
 | |
| 	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
 | |
| 		row := in.Pix[x*in.Stride:]
 | |
| 		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
 | |
| 			var rgba [4]float32
 | |
| 			var sum float32
 | |
| 			start := offset[y]
 | |
| 			ci := y * filterLength
 | |
| 			for i := 0; i < filterLength; i++ {
 | |
| 				if coeffs[ci+i] {
 | |
| 					xi := start + i
 | |
| 					switch {
 | |
| 					case uint(xi) < uint(maxX):
 | |
| 						xi *= 4
 | |
| 					case xi >= maxX:
 | |
| 						xi = 4 * maxX
 | |
| 					default:
 | |
| 						xi = 0
 | |
| 					}
 | |
| 					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) {
 | |
| 	newBounds := out.Bounds()
 | |
| 	maxX := in.Bounds().Dx() - 1
 | |
| 
 | |
| 	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
 | |
| 		row := in.Pix[x*in.Stride:]
 | |
| 		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
 | |
| 			var rgba [4]float32
 | |
| 			var sum float32
 | |
| 			start := offset[y]
 | |
| 			ci := y * filterLength
 | |
| 			for i := 0; i < filterLength; i++ {
 | |
| 				if coeffs[ci+i] {
 | |
| 					xi := start + i
 | |
| 					switch {
 | |
| 					case uint(xi) < uint(maxX):
 | |
| 						xi *= 8
 | |
| 					case xi >= maxX:
 | |
| 						xi = 8 * maxX
 | |
| 					default:
 | |
| 						xi = 0
 | |
| 					}
 | |
| 					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 nearestNRGBA64(in *image.NRGBA64, out *image.NRGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
 | |
| 	newBounds := out.Bounds()
 | |
| 	maxX := in.Bounds().Dx() - 1
 | |
| 
 | |
| 	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
 | |
| 		row := in.Pix[x*in.Stride:]
 | |
| 		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
 | |
| 			var rgba [4]float32
 | |
| 			var sum float32
 | |
| 			start := offset[y]
 | |
| 			ci := y * filterLength
 | |
| 			for i := 0; i < filterLength; i++ {
 | |
| 				if coeffs[ci+i] {
 | |
| 					xi := start + i
 | |
| 					switch {
 | |
| 					case uint(xi) < uint(maxX):
 | |
| 						xi *= 8
 | |
| 					case xi >= maxX:
 | |
| 						xi = 8 * maxX
 | |
| 					default:
 | |
| 						xi = 0
 | |
| 					}
 | |
| 					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) {
 | |
| 	newBounds := out.Bounds()
 | |
| 	maxX := in.Bounds().Dx() - 1
 | |
| 
 | |
| 	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
 | |
| 		row := in.Pix[x*in.Stride:]
 | |
| 		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
 | |
| 			var gray float32
 | |
| 			var sum float32
 | |
| 			start := offset[y]
 | |
| 			ci := y * filterLength
 | |
| 			for i := 0; i < filterLength; i++ {
 | |
| 				if coeffs[ci+i] {
 | |
| 					xi := start + i
 | |
| 					switch {
 | |
| 					case xi < 0:
 | |
| 						xi = 0
 | |
| 					case xi >= maxX:
 | |
| 						xi = maxX
 | |
| 					}
 | |
| 					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) {
 | |
| 	newBounds := out.Bounds()
 | |
| 	maxX := in.Bounds().Dx() - 1
 | |
| 
 | |
| 	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
 | |
| 		row := in.Pix[x*in.Stride:]
 | |
| 		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
 | |
| 			var gray float32
 | |
| 			var sum float32
 | |
| 			start := offset[y]
 | |
| 			ci := y * filterLength
 | |
| 			for i := 0; i < filterLength; i++ {
 | |
| 				if coeffs[ci+i] {
 | |
| 					xi := start + i
 | |
| 					switch {
 | |
| 					case uint(xi) < uint(maxX):
 | |
| 						xi *= 2
 | |
| 					case xi >= maxX:
 | |
| 						xi = 2 * maxX
 | |
| 					default:
 | |
| 						xi = 0
 | |
| 					}
 | |
| 					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)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 |