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  }