github.com/aloncn/graphics-go@v0.0.1/graphics/affine.go (about) 1 // Copyright 2011 The Graphics-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 graphics 6 7 import ( 8 "code.google.com/p/graphics-go/graphics/interp" 9 "errors" 10 "image" 11 "image/draw" 12 "math" 13 ) 14 15 // I is the identity Affine transform matrix. 16 var I = Affine{ 17 1, 0, 0, 18 0, 1, 0, 19 0, 0, 1, 20 } 21 22 // Affine is a 3x3 2D affine transform matrix. 23 // M(i,j) is Affine[i*3+j]. 24 type Affine [9]float64 25 26 // Mul returns the multiplication of two affine transform matrices. 27 func (a Affine) Mul(b Affine) Affine { 28 return Affine{ 29 a[0]*b[0] + a[1]*b[3] + a[2]*b[6], 30 a[0]*b[1] + a[1]*b[4] + a[2]*b[7], 31 a[0]*b[2] + a[1]*b[5] + a[2]*b[8], 32 a[3]*b[0] + a[4]*b[3] + a[5]*b[6], 33 a[3]*b[1] + a[4]*b[4] + a[5]*b[7], 34 a[3]*b[2] + a[4]*b[5] + a[5]*b[8], 35 a[6]*b[0] + a[7]*b[3] + a[8]*b[6], 36 a[6]*b[1] + a[7]*b[4] + a[8]*b[7], 37 a[6]*b[2] + a[7]*b[5] + a[8]*b[8], 38 } 39 } 40 41 func (a Affine) transformRGBA(dst *image.RGBA, src *image.RGBA, i interp.RGBA) error { 42 srcb := src.Bounds() 43 b := dst.Bounds() 44 for y := b.Min.Y; y < b.Max.Y; y++ { 45 for x := b.Min.X; x < b.Max.X; x++ { 46 sx, sy := a.pt(x, y) 47 if inBounds(srcb, sx, sy) { 48 c := i.RGBA(src, sx, sy) 49 off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4 50 dst.Pix[off+0] = c.R 51 dst.Pix[off+1] = c.G 52 dst.Pix[off+2] = c.B 53 dst.Pix[off+3] = c.A 54 } 55 } 56 } 57 return nil 58 } 59 60 // Transform applies the affine transform to src and produces dst. 61 func (a Affine) Transform(dst draw.Image, src image.Image, i interp.Interp) error { 62 if dst == nil { 63 return errors.New("graphics: dst is nil") 64 } 65 if src == nil { 66 return errors.New("graphics: src is nil") 67 } 68 69 // RGBA fast path. 70 dstRGBA, dstOk := dst.(*image.RGBA) 71 srcRGBA, srcOk := src.(*image.RGBA) 72 interpRGBA, interpOk := i.(interp.RGBA) 73 if dstOk && srcOk && interpOk { 74 return a.transformRGBA(dstRGBA, srcRGBA, interpRGBA) 75 } 76 77 srcb := src.Bounds() 78 b := dst.Bounds() 79 for y := b.Min.Y; y < b.Max.Y; y++ { 80 for x := b.Min.X; x < b.Max.X; x++ { 81 sx, sy := a.pt(x, y) 82 if inBounds(srcb, sx, sy) { 83 dst.Set(x, y, i.Interp(src, sx, sy)) 84 } 85 } 86 } 87 return nil 88 } 89 90 func inBounds(b image.Rectangle, x, y float64) bool { 91 if x < float64(b.Min.X) || x >= float64(b.Max.X) { 92 return false 93 } 94 if y < float64(b.Min.Y) || y >= float64(b.Max.Y) { 95 return false 96 } 97 return true 98 } 99 100 func (a Affine) pt(x0, y0 int) (x1, y1 float64) { 101 fx := float64(x0) + 0.5 102 fy := float64(y0) + 0.5 103 x1 = fx*a[0] + fy*a[1] + a[2] 104 y1 = fx*a[3] + fy*a[4] + a[5] 105 return x1, y1 106 } 107 108 // TransformCenter applies the affine transform to src and produces dst. 109 // Equivalent to 110 // a.CenterFit(dst, src).Transform(dst, src, i). 111 func (a Affine) TransformCenter(dst draw.Image, src image.Image, i interp.Interp) error { 112 if dst == nil { 113 return errors.New("graphics: dst is nil") 114 } 115 if src == nil { 116 return errors.New("graphics: src is nil") 117 } 118 119 return a.CenterFit(dst.Bounds(), src.Bounds()).Transform(dst, src, i) 120 } 121 122 // Scale produces a scaling transform of factors x and y. 123 func (a Affine) Scale(x, y float64) Affine { 124 return a.Mul(Affine{ 125 1 / x, 0, 0, 126 0, 1 / y, 0, 127 0, 0, 1, 128 }) 129 } 130 131 // Rotate produces a clockwise rotation transform of angle, in radians. 132 func (a Affine) Rotate(angle float64) Affine { 133 s, c := math.Sincos(angle) 134 return a.Mul(Affine{ 135 +c, +s, +0, 136 -s, +c, +0, 137 +0, +0, +1, 138 }) 139 } 140 141 // Shear produces a shear transform by the slopes x and y. 142 func (a Affine) Shear(x, y float64) Affine { 143 d := 1 - x*y 144 return a.Mul(Affine{ 145 +1 / d, -x / d, 0, 146 -y / d, +1 / d, 0, 147 0, 0, 1, 148 }) 149 } 150 151 // Translate produces a translation transform with pixel distances x and y. 152 func (a Affine) Translate(x, y float64) Affine { 153 return a.Mul(Affine{ 154 1, 0, -x, 155 0, 1, -y, 156 0, 0, +1, 157 }) 158 } 159 160 // Center produces the affine transform, centered around the provided point. 161 func (a Affine) Center(x, y float64) Affine { 162 return I.Translate(-x, -y).Mul(a).Translate(x, y) 163 } 164 165 // CenterFit produces the affine transform, centered around the rectangles. 166 // It is equivalent to 167 // I.Translate(-<center of src>).Mul(a).Translate(<center of dst>) 168 func (a Affine) CenterFit(dst, src image.Rectangle) Affine { 169 dx := float64(dst.Min.X) + float64(dst.Dx())/2 170 dy := float64(dst.Min.Y) + float64(dst.Dy())/2 171 sx := float64(src.Min.X) + float64(src.Dx())/2 172 sy := float64(src.Min.Y) + float64(src.Dy())/2 173 return I.Translate(-sx, -sy).Mul(a).Translate(dx, dy) 174 }