func uploadTexture_NRGBA32(img *image.NRGBA) gl.Texture { b := img.Bounds() data := make([]uint8, b.Max.X*b.Max.Y*4) for y := 0; y < b.Max.Y; y++ { for x := 0; x < b.Max.X; x++ { p := img.At(x, y) offset := y*b.Max.X*4 + x*4 r, g, b, a := p.RGBA() data[offset+0] = uint8(r) data[offset+1] = uint8(g) data[offset+2] = uint8(b) data[offset+3] = uint8(a) } } id := gl.GenTexture() id.Bind(gl.TEXTURE_2D) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, b.Max.X, b.Max.Y, 0, gl.RGBA, gl.UNSIGNED_BYTE, data) if gl.GetError() != gl.NO_ERROR { id.Delete() panic(errors.New("Failed to load a texture")) return 0 } return id }
func Resized(ext string, data []byte, width, height int) (resized []byte, w int, h int) { if width == 0 && height == 0 { return data, 0, 0 } srcImage, _, err := image.Decode(bytes.NewReader(data)) if err == nil { bounds := srcImage.Bounds() var dstImage *image.NRGBA if bounds.Dx() > width && width != 0 || bounds.Dy() > height && height != 0 { if width == height && bounds.Dx() != bounds.Dy() { dstImage = imaging.Thumbnail(srcImage, width, height, imaging.Lanczos) w, h = width, height } else { dstImage = imaging.Resize(srcImage, width, height, imaging.Lanczos) } } else { return data, bounds.Dx(), bounds.Dy() } var buf bytes.Buffer switch ext { case ".png": png.Encode(&buf, dstImage) case ".jpg", ".jpeg": jpeg.Encode(&buf, dstImage, nil) case ".gif": gif.Encode(&buf, dstImage, nil) } return buf.Bytes(), dstImage.Bounds().Dx(), dstImage.Bounds().Dy() } else { glog.Error(err) } return data, 0, 0 }
func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image.NRGBA { srcBounds := src.Bounds() srcW := srcBounds.Max.X srcH := srcBounds.Max.Y dstW := width dstH := srcH dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) weights := precomputeWeights(dstW, srcW, filter) parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { for dstX := 0; dstX < dstW; dstX++ { var c [4]int32 for _, iw := range weights[dstX].iwpairs { i := dstY*src.Stride + iw.i*4 c[0] += int32(src.Pix[i+0]) * iw.w c[1] += int32(src.Pix[i+1]) * iw.w c[2] += int32(src.Pix[i+2]) * iw.w c[3] += int32(src.Pix[i+3]) * iw.w } j := dstY*dst.Stride + dstX*4 sum := weights[dstX].wsum dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5)) dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5)) dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5)) dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5)) } } }) return dst }
// 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.Max.X srcH := srcBounds.Max.Y dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) dx := float64(srcW) / float64(dstW) dy := float64(srcH) / float64(dstH) Parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { fy := (float64(dstY)+0.5)*dy - 0.5 for dstX := 0; dstX < dstW; dstX++ { fx := (float64(dstX)+0.5)*dx - 0.5 srcX := int(math.Min(math.Max(math.Floor(fx+0.5), 0.0), float64(srcW))) srcY := int(math.Min(math.Max(math.Floor(fy+0.5), 0.0), float64(srcH))) srcOff := srcY*src.Stride + srcX*4 dstOff := dstY*dst.Stride + dstX*4 copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4]) } } }) return dst }
func Resize(context *common.AppContext, source *image.NRGBA, width, height int) *image.NRGBA { //Naive nn resize. destinationW, destinationH := width, height sourceBounds := source.Bounds() sourceW := sourceBounds.Max.X sourceH := sourceBounds.Max.Y destination := image.NewNRGBA(image.Rect(0, 0, destinationW, destinationH)) dx := float64(sourceW) / float64(destinationW) dy := float64(sourceH) / float64(destinationH) for destinationY := 0; destinationY < destinationH; destinationY++ { fy := (float64(destinationY)+0.5)*dy - 0.5 for destinationX := 0; destinationX < destinationW; destinationX++ { fx := (float64(destinationX)+0.5)*dx - 0.5 sourceX := int(math.Min(math.Max(math.Floor(fx+0.5), 0.0), float64(sourceW))) sourceY := int(math.Min(math.Max(math.Floor(fy+0.5), 0.0), float64(sourceH))) sourceOff := sourceY*source.Stride + sourceX*4 destinationOff := destinationY*destination.Stride + destinationX*4 copy(destination.Pix[destinationOff:destinationOff+4], source.Pix[sourceOff:sourceOff+4]) } } return destination }
func uploadTexture_NRGBA32(img *image.NRGBA) gl.Texture { b := img.Bounds() data := make([]uint8, b.Max.X*b.Max.Y*4) for y := 0; y < b.Max.Y; y++ { for x := 0; x < b.Max.X; x++ { p := &img.Pix[y*img.Stride+x] offset := y*b.Max.X*4 + x*4 data[offset+0] = p.R data[offset+1] = p.G data[offset+2] = p.B data[offset+3] = p.A } } id := gl.GenTexture() id.Bind(gl.TEXTURE_2D) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, b.Max.X, b.Max.Y, 0, gl.RGBA, data) if gl.GetError() != gl.NO_ERROR { id.Delete() panic(os.NewError("Failed to load a texture")) return 0 } return id }
// 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 resizeRGBA(in *image.RGBA, out *image.NRGBA, scale float64, coeffs []int16, 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]int32 var sum int32 start := offset[y] ci := y * filterLength for i := 0; i < filterLength; i++ { coeff := coeffs[ci+i] if coeff != 0 { xi := start + i switch { case uint(xi) < uint(maxX): xi *= 4 case xi >= maxX: xi = 4 * maxX default: xi = 0 } r := uint32(row[xi+0]) g := uint32(row[xi+1]) b := uint32(row[xi+2]) a := uint32(row[xi+3]) // reverse alpha-premultiplication. if a != 0 { r *= 0xffff r /= a g *= 0xffff g /= a b *= 0xffff b /= a } rgba[0] += int32(coeff) * int32(r) rgba[1] += int32(coeff) * int32(g) rgba[2] += int32(coeff) * int32(b) rgba[3] += int32(coeff) * int32(a) sum += int32(coeff) } } xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4 out.Pix[xo+0] = clampUint8(rgba[0] / sum) out.Pix[xo+1] = clampUint8(rgba[1] / sum) out.Pix[xo+2] = clampUint8(rgba[2] / sum) out.Pix[xo+3] = clampUint8(rgba[3] / sum) } } }
func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA { radius := len(kernel) - 1 width := src.Bounds().Max.X height := src.Bounds().Max.Y dst := image.NewNRGBA(image.Rect(0, 0, width, height)) parallel(height, func(partStart, partEnd int) { for y := partStart; y < partEnd; y++ { start := y - radius if start < 0 { start = 0 } end := y + radius if end > height-1 { end = height - 1 } weightSum := 0.0 for iy := start; iy <= end; iy++ { weightSum += kernel[absint(y-iy)] } for x := 0; x < width; x++ { r, g, b, a := 0.0, 0.0, 0.0, 0.0 for iy := start; iy <= end; iy++ { weight := kernel[absint(y-iy)] i := iy*src.Stride + x*4 wa := float64(src.Pix[i+3]) * weight r += float64(src.Pix[i+0]) * wa g += float64(src.Pix[i+1]) * wa b += float64(src.Pix[i+2]) * wa a += wa } r = math.Min(math.Max(r/a, 0.0), 255.0) g = math.Min(math.Max(g/a, 0.0), 255.0) b = math.Min(math.Max(b/a, 0.0), 255.0) a = math.Min(math.Max(a/weightSum, 0.0), 255.0) j := y*dst.Stride + x*4 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 }
// 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 DrawTextOnImage(text string, font *truetype.Font, img *image.NRGBA, size, x, y int) { c := freetype.NewContext() c.SetDPI(120) c.SetFont(font) c.SetFontSize(float64(size)) c.SetClip(img.Bounds()) c.SetDst(img) c.SetSrc(image.Black) pt := freetype.Pt(x, y+int(c.PointToFix32(float64(size))>>8)) c.DrawString(text, pt) }
func invertImageNrgba(nrgba *image.NRGBA) { bounds := nrgba.Bounds() for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { c := nrgba.At(x, y).(color.NRGBA) c.R = 255 - c.R c.G = 255 - c.G c.B = 255 - c.B nrgba.SetNRGBA(x, y, c) } } }
func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA { radius := len(kernel) - 1 width := src.Bounds().Max.X height := src.Bounds().Max.Y dst := image.NewNRGBA(image.Rect(0, 0, width, height)) parallel(width, func(partStart, partEnd int) { for x := partStart; x < partEnd; x++ { start := x - radius if start < 0 { start = 0 } end := x + radius if end > width-1 { end = width - 1 } weightSum := 0.0 for ix := start; ix <= end; ix++ { weightSum += kernel[absint(x-ix)] } for y := 0; y < height; y++ { r, g, b, a := 0.0, 0.0, 0.0, 0.0 for ix := start; ix <= end; ix++ { weight := kernel[absint(x-ix)] i := y*src.Stride + ix*4 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 := y*dst.Stride + x*4 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 }
// MakeImage makes an image from an image.NRGBA. func MakeImage(i *image.NRGBA) (img Image) { img.Width, img.Height = i.Bounds().Dx(), i.Bounds().Dy() img.tex = gl.GenTexture() img.tex.Bind(gl.TEXTURE_2D) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexImage2D(gl.TEXTURE_2D, 0, 4, img.Width, img.Height, 0, gl.RGBA, gl.UNSIGNED_BYTE, i.Pix) return }
func texFromImage(rend *C.SDL_Renderer, img *image.NRGBA) *C.SDL_Texture { b := img.Bounds() w, h := b.Dx(), b.Dy() fmt := C.SDL_PIXELFORMAT_ABGR8888 acc := C.SDL_TEXTUREACCESS_STATIC tex := C.SDL_CreateTexture(rend, C.Uint32(fmt), C.int(acc), C.int(w), C.int(h)) if tex == nil { panic(sdlError()) } if C.SDL_UpdateTexture(tex, nil, unsafe.Pointer(&img.Pix[0]), C.int(img.Stride)) < 0 { panic(sdlError()) } if C.SDL_SetTextureBlendMode(tex, C.SDL_BLENDMODE_BLEND) < 0 { panic(sdlError()) } return tex }
func calculateImg(img *image.NRGBA, pointX, pointY, zoom float64, julia bool, maxIter int) { minCx := -2. minCy := -2. if !julia { minCx = pointX minCy = pointY } bounds := img.Bounds() stepX := math.Abs(minCx-2.) / float64(bounds.Dx()) / zoom stepY := math.Abs(minCy-2.) / float64(bounds.Dy()) / zoom pal := paletteToNRGBA(palette.Rainbow(maxIter, 0, 1, 1, 1, 1)) for y := bounds.Min.Y; y < bounds.Max.Y; y++ { cy := minCy + float64(y)*stepY for x := bounds.Min.X; x < bounds.Max.X; x++ { img.SetNRGBA(x, y, pal[pointIteration(minCx+float64(x)*stepX, cy, pointX, pointY, julia, maxIter)]) } } }
func (s *Scene) Render(img *image.NRGBA) { rect := img.Bounds() w, h := float64(rect.Dx()), float64(rect.Dy()) i_max, j_max := rect.Dx(), rect.Dy() for i := 0; i < i_max; i++ { u := (float64(i) - w/2.0) / (w / 2.0) for j := 0; j < j_max; j++ { // (h / w) term is needed to correct for nonunity aspect ratios v := (float64(j) - h/2.0) / (h / 2.0) * (h / w) if *debug { //fmt.Printf("%d/%d\n", i * j_max + j, i_max * j_max) } s.SetColor(i, j, u, v, img) } } }
func skewVertical(src *image.NRGBA, degrees float64) *image.NRGBA { bounds := src.Bounds() maxY := bounds.Max.Y maxX := bounds.Max.X * 4 distance := float64(bounds.Max.X) * math.Tan(degrees) shouldFlip := false if distance < 0 { distance = -distance shouldFlip = true } newHeight := maxY + int(1+distance) dst := image.NewNRGBA(image.Rect(0, 0, bounds.Max.X, newHeight)) step := distance for x := 0; x < maxX; x += 4 { for row := 0; row < maxY; row += 1 { srcPx := row*src.Stride + x dstLower := (int(step)+row)*dst.Stride + x dstUpper := dstLower + dst.Stride _, delta := math.Modf(step) if src.Pix[srcPx+3] != 0 { dst.Pix[dstLower+0] += uint8(float64(src.Pix[srcPx+0]) * (1 - delta)) dst.Pix[dstLower+1] += uint8(float64(src.Pix[srcPx+1]) * (1 - delta)) dst.Pix[dstLower+2] += uint8(float64(src.Pix[srcPx+2]) * (1 - delta)) dst.Pix[dstLower+3] += uint8(float64(src.Pix[srcPx+3]) * (1 - delta)) dst.Pix[dstUpper+0] += uint8(float64(src.Pix[srcPx+0]) * delta) dst.Pix[dstUpper+1] += uint8(float64(src.Pix[srcPx+1]) * delta) dst.Pix[dstUpper+2] += uint8(float64(src.Pix[srcPx+2]) * delta) dst.Pix[dstUpper+3] += uint8(float64(src.Pix[srcPx+3]) * delta) } } step -= distance / float64(bounds.Max.X) } if shouldFlip { return imaging.FlipH(dst) } else { return dst } }
func mergeTextures(normalTexture, litTexture *image.NRGBA) { normSize := normalTexture.Bounds().Size() litSize := litTexture.Bounds().Size() if normSize.X != litSize.X || normSize.Y != litSize.Y { //Größen sind unterschiedlich --> normale Textur auf Größe der Nachttextur skalieren normalTexture = imaging.Resize(normalTexture, litSize.X, litSize.Y, imaging.Linear) } for x := 0; x < litSize.X; x++ { for y := 0; y < litSize.Y; y++ { i := y*normalTexture.Stride + x*4 j := y*litTexture.Stride + x*4 litTexture.Pix[j+0] = mergePixel(normalTexture.Pix[i+0], litTexture.Pix[j+0], 1.0) litTexture.Pix[j+1] = mergePixel(normalTexture.Pix[i+1], litTexture.Pix[j+1], 1.0) litTexture.Pix[j+2] = mergePixel(normalTexture.Pix[i+2], litTexture.Pix[j+2], 0.7) litTexture.Pix[j+3] = normalTexture.Pix[i+3] } } }
// 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 } } } }
// Encode encoded the image in SKTEXT format. func Encode(w io.Writer, m *image.NRGBA) error { fmt.Fprintf(w, "%s%d %d\n", skTextHeader, m.Rect.Dx(), m.Rect.Dy()) height := m.Bounds().Dy() for i := 0; i < len(m.Pix); i += 4 { _, err := fmt.Fprintf(w, "0x%02x%02x%02x%02x", m.Pix[i+0], m.Pix[i+1], m.Pix[i+2], m.Pix[i+3]) if err != nil { return err } // Add whitespace. if (i > 0 || m.Stride == 4) && (i+4)%m.Stride == 0 { // Don't add a trailing \n to the very last line. if (i+4)/m.Stride < height { fmt.Fprintln(w) } } else { fmt.Fprint(w, " ") } } return nil }
func resize32(src *image.NRGBA, dstW, dstH int) image.Image { srcRect := src.Bounds() srcW := srcRect.Dx() srcH := srcRect.Dy() dstW64, dstH64 := uint64(dstW), uint64(dstH) srcW64, srcH64 := uint64(srcW), uint64(srcH) dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) var x, y uint64 dstI := 0 for y = 0; y < dstH64; y++ { srcY := int(y * srcH64 / dstH64) for x = 0; x < dstW64; x++ { srcX := int(x * srcW64 / dstW64) srcI := 4 * (srcY*srcW + srcX) copy(dst.Pix[dstI:dstI+4], src.Pix[srcI:srcI+4]) dstI += 4 } } return dst }
func resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image.NRGBA { srcBounds := src.Bounds() srcW := srcBounds.Max.X srcH := srcBounds.Max.Y dstW := srcW dstH := height dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) weights := precomputeWeights(dstH, srcH, filter) parallel(dstW, func(partStart, partEnd int) { for dstX := partStart; dstX < partEnd; dstX++ { for dstY := 0; dstY < dstH; dstY++ { var c [4]int64 for _, iw := range weights[dstY].iwpairs { i := iw.i*src.Stride + dstX*4 a := int64(src.Pix[i+3]) * int64(iw.w) c[0] += int64(src.Pix[i+0]) * a c[1] += int64(src.Pix[i+1]) * a c[2] += int64(src.Pix[i+2]) * a c[3] += a } j := dstY*dst.Stride + dstX*4 sum := weights[dstY].wsum dst.Pix[j+0] = clampint32(int32(float64(c[0])/float64(c[3]) + 0.5)) dst.Pix[j+1] = clampint32(int32(float64(c[1])/float64(c[3]) + 0.5)) dst.Pix[j+2] = clampint32(int32(float64(c[2])/float64(c[3]) + 0.5)) dst.Pix[j+3] = clampint32(int32(float64(c[3])/float64(sum) + 0.5)) } } }) return dst }
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 (l *windowsAssetloader) loadResources() { resourceData, err := payload.Read() check(err) l.resources, err = blob.Read(bytes.NewBuffer(resourceData)) // load the texture atlas atlas, found := l.resources.GetByID("atlas") if !found { panic("texture atlas not found in resources") } ping, err := png.Decode(bytes.NewReader(atlas)) check(err) var nrgba *image.NRGBA if asNRGBA, ok := ping.(*image.NRGBA); ok { nrgba = asNRGBA } else { nrgba = image.NewNRGBA(ping.Bounds()) draw.Draw(nrgba, nrgba.Bounds(), ping, image.ZP, draw.Src) } texture, err := l.device.CreateTexture( uint(nrgba.Bounds().Dx()), uint(nrgba.Bounds().Dy()), 1, d3d9.USAGE_SOFTWAREPROCESSING, d3d9.FMT_A8R8G8B8, d3d9.POOL_MANAGED, nil, ) check(err) lockedRect, err := texture.LockRect(0, nil, d3d9.LOCK_DISCARD) check(err) lockedRect.SetAllBytes(nrgba.Pix, nrgba.Stride) check(texture.UnlockRect(0)) l.textureAtlas = texture l.textureAtlasBounds = nrgba.Bounds() }
func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image.NRGBA { srcBounds := src.Bounds() srcW := srcBounds.Max.X srcH := srcBounds.Max.Y 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) Parallel(dstW, func(partStart, partEnd int) { weights := make([]float64, int(rX+2)*2) for dstX := partStart; dstX < partEnd; dstX++ { fX := (float64(dstX)+0.5)*dX - 0.5 startX := int(math.Ceil(fX - rX)) if startX < 0 { startX = 0 } endX := int(math.Floor(fX + rX)) if endX > srcW-1 { endX = srcW - 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++ { r, g, b, a := 0.0, 0.0, 0.0, 0.0 for x := startX; x <= endX; x++ { weight := weights[x-startX] i := dstY*src.Stride + x*4 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 := dstY*dst.Stride + dstX*4 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 resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image.NRGBA { srcBounds := src.Bounds() srcW := srcBounds.Max.X srcH := 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) Parallel(dstH, func(partStart, partEnd int) { weights := make([]float64, int(rY+2)*2) for dstY := partStart; dstY < partEnd; dstY++ { fY := (float64(dstY)+0.5)*dY - 0.5 startY := int(math.Ceil(fY - rY)) if startY < 0 { startY = 0 } endY := int(math.Floor(fY + rY)) if endY > srcH-1 { endY = srcH - 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++ { r, g, b, a := 0.0, 0.0, 0.0, 0.0 for y := startY; y <= endY; y++ { weight := weights[y-startY] i := y*src.Stride + dstX*4 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 := dstY*dst.Stride + dstX*4 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 makeImg(s string, b string, h int, qx int, qy int, u int) (*image.NRGBA, error) { var ( width int height int canvas *image.NRGBA k int v rune ) if u <= 0 { u = 1 } if qx <= 0 { qx = 5 } if qy <= 0 { qy = 3 } if h <= 0 { h = 100 } width = qx*2 + len(b)*u height = qy*2 + h fontSize := 16 canvas = image.NewNRGBA(image.Rect(0, 0, width, height+fontSize+5)) drawRect(canvas, image.Rect(0, 0, width, height+fontSize+5), color.RGBA{255, 255, 255, 255}) for k, v = range b { if v == 49 { drawRect(canvas, image.Rect(qx+k*u, qy, qx+k*u+u, qy+h), color.RGBA{0, 0, 0, 255}) } } data, err := ioutil.ReadFile("./fonts/luxisr.ttf") if err != nil { return nil, err } font, err := freetype.ParseFont(data) if err != nil { return nil, err } c := freetype.NewContext() c.SetDst(canvas) c.SetClip(canvas.Bounds()) c.SetSrc(image.Black) c.SetFont(font) c.SetFontSize(float64(fontSize)) s = expandStr(s) p, _ := c.DrawString(s, freetype.Pt(0, height+fontSize+5)) drawRect(canvas, image.Rect(0, height, width, height+fontSize+5), color.RGBA{255, 255, 255, 255}) p, _ = c.DrawString(s, freetype.Pt(width/2-int(p.X)/256/2, height+fontSize)) return canvas, nil }
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 }
// 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 }