github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/sprite/portable/bilinear.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package portable
     6  
     7  import (
     8  	"image"
     9  	"image/color"
    10  	"math"
    11  )
    12  
    13  func bilinear(src image.Image, x, y float32) color.Color {
    14  	switch src := src.(type) {
    15  	case *image.RGBA:
    16  		return bilinearRGBA(src, x, y)
    17  	case *image.Alpha:
    18  		return bilinearAlpha(src, x, y)
    19  	case *image.Uniform:
    20  		return src.C
    21  	default:
    22  		return bilinearGeneral(src, x, y)
    23  	}
    24  }
    25  
    26  func bilinearGeneral(src image.Image, x, y float32) color.RGBA64 {
    27  	p := findLinearSrc(src.Bounds(), x, y)
    28  
    29  	r00, g00, b00, a00 := src.At(p.low.X, p.low.Y).RGBA()
    30  	r01, g01, b01, a01 := src.At(p.high.X, p.low.Y).RGBA()
    31  	r10, g10, b10, a10 := src.At(p.low.X, p.high.Y).RGBA()
    32  	r11, g11, b11, a11 := src.At(p.high.X, p.high.Y).RGBA()
    33  
    34  	fr := float32(r00) * p.frac00
    35  	fg := float32(g00) * p.frac00
    36  	fb := float32(b00) * p.frac00
    37  	fa := float32(a00) * p.frac00
    38  
    39  	fr += float32(r01) * p.frac01
    40  	fg += float32(g01) * p.frac01
    41  	fb += float32(b01) * p.frac01
    42  	fa += float32(a01) * p.frac01
    43  
    44  	fr += float32(r10) * p.frac10
    45  	fg += float32(g10) * p.frac10
    46  	fb += float32(b10) * p.frac10
    47  	fa += float32(a10) * p.frac10
    48  
    49  	fr += float32(r11) * p.frac11
    50  	fg += float32(g11) * p.frac11
    51  	fb += float32(b11) * p.frac11
    52  	fa += float32(a11) * p.frac11
    53  
    54  	return color.RGBA64{
    55  		R: uint16(fr + 0.5),
    56  		G: uint16(fg + 0.5),
    57  		B: uint16(fb + 0.5),
    58  		A: uint16(fa + 0.5),
    59  	}
    60  }
    61  
    62  func bilinearRGBA(src *image.RGBA, x, y float32) color.RGBA {
    63  	p := findLinearSrc(src.Bounds(), x, y)
    64  
    65  	// Slice offsets for the surrounding pixels.
    66  	off00 := src.PixOffset(p.low.X, p.low.Y)
    67  	off01 := src.PixOffset(p.high.X, p.low.Y)
    68  	off10 := src.PixOffset(p.low.X, p.high.Y)
    69  	off11 := src.PixOffset(p.high.X, p.high.Y)
    70  
    71  	fr := float32(src.Pix[off00+0]) * p.frac00
    72  	fg := float32(src.Pix[off00+1]) * p.frac00
    73  	fb := float32(src.Pix[off00+2]) * p.frac00
    74  	fa := float32(src.Pix[off00+3]) * p.frac00
    75  
    76  	fr += float32(src.Pix[off01+0]) * p.frac01
    77  	fg += float32(src.Pix[off01+1]) * p.frac01
    78  	fb += float32(src.Pix[off01+2]) * p.frac01
    79  	fa += float32(src.Pix[off01+3]) * p.frac01
    80  
    81  	fr += float32(src.Pix[off10+0]) * p.frac10
    82  	fg += float32(src.Pix[off10+1]) * p.frac10
    83  	fb += float32(src.Pix[off10+2]) * p.frac10
    84  	fa += float32(src.Pix[off10+3]) * p.frac10
    85  
    86  	fr += float32(src.Pix[off11+0]) * p.frac11
    87  	fg += float32(src.Pix[off11+1]) * p.frac11
    88  	fb += float32(src.Pix[off11+2]) * p.frac11
    89  	fa += float32(src.Pix[off11+3]) * p.frac11
    90  
    91  	return color.RGBA{
    92  		R: uint8(fr + 0.5),
    93  		G: uint8(fg + 0.5),
    94  		B: uint8(fb + 0.5),
    95  		A: uint8(fa + 0.5),
    96  	}
    97  }
    98  
    99  func bilinearAlpha(src *image.Alpha, x, y float32) color.Alpha {
   100  	p := findLinearSrc(src.Bounds(), x, y)
   101  
   102  	// Slice offsets for the surrounding pixels.
   103  	off00 := src.PixOffset(p.low.X, p.low.Y)
   104  	off01 := src.PixOffset(p.high.X, p.low.Y)
   105  	off10 := src.PixOffset(p.low.X, p.high.Y)
   106  	off11 := src.PixOffset(p.high.X, p.high.Y)
   107  
   108  	fa := float32(src.Pix[off00]) * p.frac00
   109  	fa += float32(src.Pix[off01]) * p.frac01
   110  	fa += float32(src.Pix[off10]) * p.frac10
   111  	fa += float32(src.Pix[off11]) * p.frac11
   112  
   113  	return color.Alpha{A: uint8(fa + 0.5)}
   114  }
   115  
   116  type bilinearSrc struct {
   117  	// Top-left and bottom-right interpolation sources
   118  	low, high image.Point
   119  	// Fraction of each pixel to take. The 0 suffix indicates
   120  	// top/left, and the 1 suffix indicates bottom/right.
   121  	frac00, frac01, frac10, frac11 float32
   122  }
   123  
   124  func floor(x float32) float32 { return float32(math.Floor(float64(x))) }
   125  func ceil(x float32) float32  { return float32(math.Ceil(float64(x))) }
   126  
   127  func findLinearSrc(b image.Rectangle, sx, sy float32) bilinearSrc {
   128  	maxX := float32(b.Max.X)
   129  	maxY := float32(b.Max.Y)
   130  	minX := float32(b.Min.X)
   131  	minY := float32(b.Min.Y)
   132  	lowX := floor(sx - 0.5)
   133  	lowY := floor(sy - 0.5)
   134  	if lowX < minX {
   135  		lowX = minX
   136  	}
   137  	if lowY < minY {
   138  		lowY = minY
   139  	}
   140  
   141  	highX := ceil(sx - 0.5)
   142  	highY := ceil(sy - 0.5)
   143  	if highX >= maxX {
   144  		highX = maxX - 1
   145  	}
   146  	if highY >= maxY {
   147  		highY = maxY - 1
   148  	}
   149  
   150  	// In the variables below, the 0 suffix indicates top/left, and the
   151  	// 1 suffix indicates bottom/right.
   152  
   153  	// Center of each surrounding pixel.
   154  	x00 := lowX + 0.5
   155  	y00 := lowY + 0.5
   156  	x01 := highX + 0.5
   157  	y01 := lowY + 0.5
   158  	x10 := lowX + 0.5
   159  	y10 := highY + 0.5
   160  	x11 := highX + 0.5
   161  	y11 := highY + 0.5
   162  
   163  	p := bilinearSrc{
   164  		low:  image.Pt(int(lowX), int(lowY)),
   165  		high: image.Pt(int(highX), int(highY)),
   166  	}
   167  
   168  	// Literally, edge cases. If we are close enough to the edge of
   169  	// the image, curtail the interpolation sources.
   170  	if lowX == highX && lowY == highY {
   171  		p.frac00 = 1.0
   172  	} else if sy-minY <= 0.5 && sx-minX <= 0.5 {
   173  		p.frac00 = 1.0
   174  	} else if maxY-sy <= 0.5 && maxX-sx <= 0.5 {
   175  		p.frac11 = 1.0
   176  	} else if sy-minY <= 0.5 || lowY == highY {
   177  		p.frac00 = x01 - sx
   178  		p.frac01 = sx - x00
   179  	} else if sx-minX <= 0.5 || lowX == highX {
   180  		p.frac00 = y10 - sy
   181  		p.frac10 = sy - y00
   182  	} else if maxY-sy <= 0.5 {
   183  		p.frac10 = x11 - sx
   184  		p.frac11 = sx - x10
   185  	} else if maxX-sx <= 0.5 {
   186  		p.frac01 = y11 - sy
   187  		p.frac11 = sy - y01
   188  	} else {
   189  		p.frac00 = (x01 - sx) * (y10 - sy)
   190  		p.frac01 = (sx - x00) * (y11 - sy)
   191  		p.frac10 = (x11 - sx) * (sy - y00)
   192  		p.frac11 = (sx - x10) * (sy - y01)
   193  	}
   194  
   195  	return p
   196  }