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 }