// 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 }
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) } }
// 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 }
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] } } }
// 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) }
// 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 } } } }
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 } }
// 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 }
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 }
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 }
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 }
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 }