Example #1
0
// Interpolate 4 interleaved uint8 per pixel images.
func interpolate4x8(src *image.NRGBA, dstW, dstH int) image.Image {
	srcRect := src.Bounds()
	srcW := srcRect.Dx()
	srcH := srcRect.Dy()

	ww, hh := uint64(dstW), uint64(dstH)
	dx, dy := uint64(srcW), uint64(srcH)

	n, sum := dx*dy, make([]uint64, 4*dstW*dstH)
	for y := 0; y < srcH; y++ {
		pixOffset := src.PixOffset(0, y)
		for x := 0; x < srcW; x++ {
			// Get the source pixel.
			r64 := uint64(src.Pix[pixOffset+0])
			g64 := uint64(src.Pix[pixOffset+1])
			b64 := uint64(src.Pix[pixOffset+2])
			a64 := uint64(src.Pix[pixOffset+3])
			pixOffset += 4
			// Spread the source pixel over 1 or more destination rows.
			py := uint64(y) * hh
			for remy := hh; remy > 0; {
				qy := dy - (py % dy)
				if qy > remy {
					qy = remy
				}
				// Spread the source pixel over 1 or more destination columns.
				px := uint64(x) * ww
				index := 4 * ((py/dy)*ww + (px / dx))
				for remx := ww; remx > 0; {
					qx := dx - (px % dx)
					if qx > remx {
						qx = remx
					}
					qxy := qx * qy
					sum[index+0] += r64 * qxy
					sum[index+1] += g64 * qxy
					sum[index+2] += b64 * qxy
					sum[index+3] += a64 * qxy
					index += 4
					px += qx
					remx -= qx
				}
				py += qy
				remy -= qy
			}
		}
	}
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
	for y := 0; y < dstH; y++ {
		pixOffset := dst.PixOffset(0, y)
		for x := 0; x < dstW; x++ {
			dst.Pix[pixOffset+0] = uint8(sum[pixOffset+0] / n)
			dst.Pix[pixOffset+1] = uint8(sum[pixOffset+1] / n)
			dst.Pix[pixOffset+2] = uint8(sum[pixOffset+2] / n)
			dst.Pix[pixOffset+3] = uint8(sum[pixOffset+3] / n)
			pixOffset += 4
		}
	}
	return dst
}
Example #2
0
func newSetFuncNRGBA(p *image.NRGBA) SetFunc {
	return func(x, y int, r, g, b, a uint32) {
		r, g, b, a = RGBAToNRGBA(r, g, b, a)
		i := p.PixOffset(x, y)
		p.Pix[i+0] = uint8(r >> 8)
		p.Pix[i+1] = uint8(g >> 8)
		p.Pix[i+2] = uint8(b >> 8)
		p.Pix[i+3] = uint8(a >> 8)
	}
}
Example #3
0
// Interpolate uint32/pixel images.
func interpolate1x32(src *image.NRGBA, dstW, dstH int) image.Image {
	srcRect := src.Bounds()
	srcW := srcRect.Dx()
	srcH := srcRect.Dy()

	ww, hh := uint64(dstW), uint64(dstH)
	dx, dy := uint64(srcW), uint64(srcH)

	n, sum := dx*dy, make([]uint64, dstW*dstH)
	for y := 0; y < srcH; y++ {
		pixOffset := src.PixOffset(0, y)
		for x := 0; x < srcW; x++ {
			// Get the source pixel.
			val64 := uint64(binary.BigEndian.Uint32([]byte(src.Pix[pixOffset+0 : pixOffset+4])))
			pixOffset += 4

			// Spread the source pixel over 1 or more destination rows.
			py := uint64(y) * hh
			for remy := hh; remy > 0; {
				qy := dy - (py % dy)
				if qy > remy {
					qy = remy
				}
				// Spread the source pixel over 1 or more destination columns.
				px := uint64(x) * ww
				index := (py/dy)*ww + (px / dx)
				for remx := ww; remx > 0; {
					qx := dx - (px % dx)
					if qx > remx {
						qx = remx
					}
					qxy := qx * qy
					sum[index] += val64 * qxy
					index++
					px += qx
					remx -= qx
				}
				py += qy
				remy -= qy
			}
		}
	}
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
	index := 0
	for y := 0; y < dstH; y++ {
		pixOffset := dst.PixOffset(0, y)
		for x := 0; x < dstW; x++ {
			binary.BigEndian.PutUint32(dst.Pix[pixOffset+0:pixOffset+4], uint32(sum[index]/n))
			pixOffset += 4
			index++
		}
	}
	return dst
}
Example #4
0
func convertNRGBA(dest *Image, src *image.NRGBA) {
	var x, y, i, si int
	var a uint16

	for x = dest.Rect.Min.X; x < dest.Rect.Max.X; x++ {
		for y = dest.Rect.Min.Y; y < dest.Rect.Max.Y; y++ {
			si = src.PixOffset(x, y)
			i = dest.PixOffset(x, y)
			a = uint16(src.Pix[si+3])

			dest.Pix[i+0] = uint8((uint16(src.Pix[si+2]) * a) / 0xff)
			dest.Pix[i+1] = uint8((uint16(src.Pix[si+1]) * a) / 0xff)
			dest.Pix[i+2] = uint8((uint16(src.Pix[si+0]) * a) / 0xff)
			dest.Pix[i+3] = src.Pix[si+3]
		}
	}
}
Example #5
0
// Takes an average of the biome colors of the surrounding area
func calculateBiome(bs *blocksSnapshot, x, z int, img *image.NRGBA) (byte, byte, byte) {
	count := 0
	var r, g, b int
	for xx := -2; xx <= 2; xx++ {
		for zz := -2; zz <= 2; zz++ {
			biome := bs.biome(x+xx, z+zz)
			ix := biome.ColorIndex & 0xFF
			iy := biome.ColorIndex >> 8

			offset := img.PixOffset(ix, iy)
			r += int(img.Pix[offset])
			g += int(img.Pix[offset+1])
			b += int(img.Pix[offset+2])
			count++
		}
	}
	return byte(r / count), byte(g / count), byte(b / count)
}
Example #6
0
// Draws the "src" onto the "dst" image at the given x/y bounds, maintaining
// the original size. Pixels with have an alpha of 0x00 are not draw, and
// all others are drawn with an alpha of 0xFF
func fastDraw(dst *image.NRGBA, src *image.NRGBA, x, y int) {
	bounds := src.Bounds()
	maxY := bounds.Max.Y
	maxX := bounds.Max.X * 4

	pointer := dst.PixOffset(x, y)
	for row := 0; row < maxY; row += 1 {
		for i := 0; i < maxX; i += 4 {
			srcPx := row*src.Stride + i
			dstPx := row*dst.Stride + i + pointer
			if src.Pix[srcPx+3] != 0 {
				dst.Pix[dstPx+0] = src.Pix[srcPx+0]
				dst.Pix[dstPx+1] = src.Pix[srcPx+1]
				dst.Pix[dstPx+2] = src.Pix[srcPx+2]
				dst.Pix[dstPx+3] = 0xFF
			}
		}
	}
}
Example #7
0
func newAtFuncNRGBA(p *image.NRGBA) AtFunc {
	return func(x, y int) (r, g, b, a uint32) {
		i := p.PixOffset(x, y)
		a = uint32(p.Pix[i+3])
		a |= a << 8
		if a == 0 {
			return
		}
		r = uint32(p.Pix[i+0])
		r |= r << 8
		g = uint32(p.Pix[i+1])
		g |= g << 8
		b = uint32(p.Pix[i+2])
		b |= b << 8
		if a == 0xffff {
			return
		}
		r = r * a / 0xffff
		g = g * a / 0xffff
		b = b * a / 0xffff
		return
	}
}
Example #8
0
// fast nearest-neighbor resize, no filtering
func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA {
	dstW, dstH := width, height

	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()
	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxX := srcBounds.Max.X
	srcMaxY := srcBounds.Max.Y

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))

	dx := float64(srcW) / float64(dstW)
	dy := float64(srcH) / float64(dstH)

	// divide image to parts for parallel processing
	numGoroutines := runtime.NumCPU()
	goMaxProcs := runtime.GOMAXPROCS(0)
	if numGoroutines > goMaxProcs {
		numGoroutines = goMaxProcs
	}
	if numGoroutines > dstH {
		numGoroutines = dstH
	}
	partSize := dstH / numGoroutines

	doneChan := make(chan bool, numGoroutines)

	for part := 0; part < numGoroutines; part++ {
		partStart := part * partSize
		partEnd := (part + 1) * partSize
		if part == numGoroutines-1 {
			partEnd = dstH
		}

		go func(partStart, partEnd int) {

			for dstY := partStart; dstY < partEnd; dstY++ {
				fy := float64(srcMinY) + (float64(dstY)+0.5)*dy - 0.5

				for dstX := 0; dstX < dstW; dstX++ {
					fx := float64(srcMinX) + (float64(dstX)+0.5)*dx - 0.5

					srcX := int(math.Min(math.Max(math.Floor(fx+0.5), float64(srcMinX)), float64(srcMaxX)))
					srcY := int(math.Min(math.Max(math.Floor(fy+0.5), float64(srcMinY)), float64(srcMaxY)))

					srcOffset := src.PixOffset(srcX, srcY)
					dstOffset := dst.PixOffset(dstX, dstY)

					dst.Pix[dstOffset+0] = src.Pix[srcOffset+0]
					dst.Pix[dstOffset+1] = src.Pix[srcOffset+1]
					dst.Pix[dstOffset+2] = src.Pix[srcOffset+2]
					dst.Pix[dstOffset+3] = src.Pix[srcOffset+3]
				}
			}

			doneChan <- true
		}(partStart, partEnd)
	}

	// wait for goroutines to finish
	for part := 0; part < numGoroutines; part++ {
		<-doneChan
	}

	return dst
}
Example #9
0
func resizeVertical(src *image.NRGBA, height int, filter resamplingFilter) *image.NRGBA {
	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()
	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxY := srcBounds.Max.Y

	dstW := srcW
	dstH := height

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))

	dY := float64(srcH) / float64(dstH)
	scaleY := math.Max(dY, 1.0)
	rY := math.Ceil(scaleY * filter.Support)

	// divide image to parts for parallel processing
	numGoroutines := runtime.NumCPU()
	goMaxProcs := runtime.GOMAXPROCS(0)
	if numGoroutines > goMaxProcs {
		numGoroutines = goMaxProcs
	}
	if numGoroutines > dstH {
		numGoroutines = dstH
	}
	partSize := dstH / numGoroutines

	doneChan := make(chan bool, numGoroutines)

	for part := 0; part < numGoroutines; part++ {
		partStart := part * partSize
		partEnd := (part + 1) * partSize
		if part == numGoroutines-1 {
			partEnd = dstH
		}

		go func(partStart, partEnd int) {

			for dstY := partStart; dstY < partEnd; dstY++ {
				fY := float64(srcMinY) + (float64(dstY)+0.5)*dY - 0.5

				startY := int(math.Ceil(fY - rY))
				if startY < srcMinY {
					startY = srcMinY
				}
				endY := int(math.Floor(fY + rY))
				if endY > srcMaxY-1 {
					endY = srcMaxY - 1
				}

				// cache weights
				weightSum := 0.0
				weights := make([]float64, int(rY+2)*2)
				for y := startY; y <= endY; y++ {
					w := filter.Kernel((float64(y) - fY) / scaleY)
					weightSum += w
					weights[y-startY] = w
				}

				for dstX := 0; dstX < dstW; dstX++ {
					srcX := srcMinX + dstX

					r, g, b, a := 0.0, 0.0, 0.0, 0.0
					for y := startY; y <= endY; y++ {
						weight := weights[y-startY]
						i := src.PixOffset(srcX, y)
						r += float64(src.Pix[i+0]) * weight
						g += float64(src.Pix[i+1]) * weight
						b += float64(src.Pix[i+2]) * weight
						a += float64(src.Pix[i+3]) * weight
					}

					r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
					g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
					b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
					a = math.Min(math.Max(a/weightSum, 0.0), 255.0)

					j := dst.PixOffset(dstX, dstY)
					dst.Pix[j+0] = uint8(r + 0.5)
					dst.Pix[j+1] = uint8(g + 0.5)
					dst.Pix[j+2] = uint8(b + 0.5)
					dst.Pix[j+3] = uint8(a + 0.5)
				}
			}

			doneChan <- true
		}(partStart, partEnd)

	}

	// wait for goroutines to finish
	for part := 0; part < numGoroutines; part++ {
		<-doneChan
	}

	return dst
}
Example #10
0
func resizeHorizontal(src *image.NRGBA, width int, filter resamplingFilter) *image.NRGBA {
	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()
	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxX := srcBounds.Max.X

	dstW := width
	dstH := srcH

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))

	dX := float64(srcW) / float64(dstW)
	scaleX := math.Max(dX, 1.0)
	rX := math.Ceil(scaleX * filter.Support)

	// divide image to parts for parallel processing
	numGoroutines := runtime.NumCPU()
	goMaxProcs := runtime.GOMAXPROCS(0)
	if numGoroutines > goMaxProcs {
		numGoroutines = goMaxProcs
	}
	if numGoroutines > dstW {
		numGoroutines = dstW
	}
	partSize := dstW / numGoroutines

	doneChan := make(chan bool, numGoroutines)

	for part := 0; part < numGoroutines; part++ {
		partStart := part * partSize
		partEnd := (part + 1) * partSize
		if part == numGoroutines-1 {
			partEnd = dstW
		}

		go func(partStart, partEnd int) {

			for dstX := partStart; dstX < partEnd; dstX++ {
				fX := float64(srcMinX) + (float64(dstX)+0.5)*dX - 0.5

				startX := int(math.Ceil(fX - rX))
				if startX < srcMinX {
					startX = srcMinX
				}
				endX := int(math.Floor(fX + rX))
				if endX > srcMaxX-1 {
					endX = srcMaxX - 1
				}

				// cache weights
				weightSum := 0.0
				weights := make([]float64, int(rX+2)*2)
				for x := startX; x <= endX; x++ {
					w := filter.Kernel((float64(x) - fX) / scaleX)
					weightSum += w
					weights[x-startX] = w
				}

				for dstY := 0; dstY < dstH; dstY++ {
					srcY := srcMinY + dstY

					r, g, b, a := 0.0, 0.0, 0.0, 0.0
					for x := startX; x <= endX; x++ {
						weight := weights[x-startX]
						i := src.PixOffset(x, srcY)
						r += float64(src.Pix[i+0]) * weight
						g += float64(src.Pix[i+1]) * weight
						b += float64(src.Pix[i+2]) * weight
						a += float64(src.Pix[i+3]) * weight
					}

					r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
					g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
					b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
					a = math.Min(math.Max(a/weightSum, 0.0), 255.0)

					j := dst.PixOffset(dstX, dstY)
					dst.Pix[j+0] = uint8(r + 0.5)
					dst.Pix[j+1] = uint8(g + 0.5)
					dst.Pix[j+2] = uint8(b + 0.5)
					dst.Pix[j+3] = uint8(a + 0.5)
				}
			}

			doneChan <- true
		}(partStart, partEnd)

	}

	// wait for goroutines to finish
	for part := 0; part < numGoroutines; part++ {
		<-doneChan
	}

	return dst
}
Example #11
0
func resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image.NRGBA {
	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()
	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxY := srcBounds.Max.Y

	dstW := srcW
	dstH := height

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))

	dY := float64(srcH) / float64(dstH)
	scaleY := math.Max(dY, 1.0)
	rY := math.Ceil(scaleY * filter.Support)
	weights := make([]float64, int(rY+2)*2)

	for dstY := 0; dstY < dstH; dstY++ {
		fY := float64(srcMinY) + (float64(dstY)+0.5)*dY - 0.5

		startY := int(math.Ceil(fY - rY))
		if startY < srcMinY {
			startY = srcMinY
		}
		endY := int(math.Floor(fY + rY))
		if endY > srcMaxY-1 {
			endY = srcMaxY - 1
		}

		// cache weights
		weightSum := 0.0
		for y := startY; y <= endY; y++ {
			w := filter.Kernel((float64(y) - fY) / scaleY)
			weightSum += w
			weights[y-startY] = w
		}

		for dstX := 0; dstX < dstW; dstX++ {
			srcX := srcMinX + dstX

			r, g, b, a := 0.0, 0.0, 0.0, 0.0
			for y := startY; y <= endY; y++ {
				weight := weights[y-startY]
				i := src.PixOffset(srcX, y)
				r += float64(src.Pix[i+0]) * weight
				g += float64(src.Pix[i+1]) * weight
				b += float64(src.Pix[i+2]) * weight
				a += float64(src.Pix[i+3]) * weight
			}

			r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
			g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
			b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
			a = math.Min(math.Max(a/weightSum, 0.0), 255.0)

			j := dst.PixOffset(dstX, dstY)
			dst.Pix[j+0] = uint8(r + 0.5)
			dst.Pix[j+1] = uint8(g + 0.5)
			dst.Pix[j+2] = uint8(b + 0.5)
			dst.Pix[j+3] = uint8(a + 0.5)
		}
	}

	return dst
}
Example #12
0
func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image.NRGBA {
	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()
	srcMinX := srcBounds.Min.X
	srcMinY := srcBounds.Min.Y
	srcMaxX := srcBounds.Max.X

	dstW := width
	dstH := srcH

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))

	dX := float64(srcW) / float64(dstW)
	scaleX := math.Max(dX, 1.0)
	rX := math.Ceil(scaleX * filter.Support)
	weights := make([]float64, int(rX+2)*2)

	for dstX := 0; dstX < dstW; dstX++ {
		fX := float64(srcMinX) + (float64(dstX)+0.5)*dX - 0.5

		startX := int(math.Ceil(fX - rX))
		if startX < srcMinX {
			startX = srcMinX
		}
		endX := int(math.Floor(fX + rX))
		if endX > srcMaxX-1 {
			endX = srcMaxX - 1
		}

		// cache weights
		weightSum := 0.0
		for x := startX; x <= endX; x++ {
			w := filter.Kernel((float64(x) - fX) / scaleX)
			weightSum += w
			weights[x-startX] = w
		}

		for dstY := 0; dstY < dstH; dstY++ {
			srcY := srcMinY + dstY

			r, g, b, a := 0.0, 0.0, 0.0, 0.0
			for x := startX; x <= endX; x++ {
				weight := weights[x-startX]
				i := src.PixOffset(x, srcY)
				r += float64(src.Pix[i+0]) * weight
				g += float64(src.Pix[i+1]) * weight
				b += float64(src.Pix[i+2]) * weight
				a += float64(src.Pix[i+3]) * weight
			}

			r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
			g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
			b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
			a = math.Min(math.Max(a/weightSum, 0.0), 255.0)

			j := dst.PixOffset(dstX, dstY)
			dst.Pix[j+0] = uint8(r + 0.5)
			dst.Pix[j+1] = uint8(g + 0.5)
			dst.Pix[j+2] = uint8(b + 0.5)
			dst.Pix[j+3] = uint8(a + 0.5)
		}
	}

	return dst
}