// decodeP6 reads a binary pixmap func decodeP6(r *bufio.Reader, img draw.Image, width, height uint) { var x, y, pix int mask := byte(readU(r)) mul := 255 / mask data := make([]byte, width*height*3) space(r) _, err := r.Read(data) check(err) for y = 0; y < int(height); y++ { for x = 0; x < int(width); x++ { img.Set(x, y, color.RGBA{ (data[pix] & mask) * mul, (data[pix+1] & mask) * mul, (data[pix+2] & mask) * mul, 0xff, }) pix += 3 } } }
func line(img draw.Image, a, b image.Point, c color.Color) { minx, maxx := minmax(a.X, b.X) miny, maxy := minmax(a.Y, b.Y) Δx := float64(b.X - a.X) Δy := float64(b.Y - a.Y) if maxx-minx > maxy-miny { d := 1 if a.X > b.X { d = -1 } for x := 0; x != b.X-a.X+d; x += d { y := int(float64(x) * Δy / Δx) img.Set(a.X+x, a.Y+y, c) } } else { d := 1 if a.Y > b.Y { d = -1 } for y := 0; y != b.Y-a.Y+d; y += d { x := int(float64(y) * Δx / Δy) img.Set(a.X+x, a.Y+y, c) } } }
func warm(out, in draw.Image, opacity uint8, colors []color.Color) { bounds := in.Bounds() collen := float64(len(colors)) wg := sync.WaitGroup{} wg.Add(bounds.Dx()) for x := bounds.Min.X; x < bounds.Max.X; x++ { go func(x int) { defer wg.Done() for y := bounds.Min.Y; y < bounds.Max.Y; y++ { col := in.At(x, y) _, _, _, alpha := col.RGBA() percent := float64(alpha) / float64(0xffff) var outcol color.Color if percent == 0 { outcol = color.Transparent } else { template := colors[int((collen-1)*(1.0-percent))] tr, tg, tb, ta := template.RGBA() ta /= 256 outalpha := uint8(float64(ta) * (float64(opacity) / 256.0)) outcol = color.NRGBA{ uint8(tr / 256), uint8(tg / 256), uint8(tb / 256), uint8(outalpha)} } out.Set(x, y, outcol) } }(x) } wg.Wait() }
// http://en.wikipedia.org/wiki/Bresenham's_line_algorithm#Simplification func line(x0, y0, x1, y1 int, c color.Color, img draw.Image) { var dx = abs(x1 - x0) var dy = abs(y1 - y0) var err = dx - dy var sx, sy = 1, 1 if x0 > x1 { sx = -1 } if y0 > y1 { sy = -1 } img.Set(x0, y0, c) for x0 != x1 || y0 != y1 { var e2 = 2 * err if e2 > -dy { err -= dy x0 += sx } if e2 < dx { err += dx y0 += sy } img.Set(x0, y0, c) } }
// Transform applies the affine transform to src and produces dst. func (a Affine) Transform(dst draw.Image, src image.Image, i interp.Interp) error { if dst == nil { return errors.New("graphics: dst is nil") } if src == nil { return errors.New("graphics: src is nil") } // RGBA fast path. dstRGBA, dstOk := dst.(*image.RGBA) srcRGBA, srcOk := src.(*image.RGBA) interpRGBA, interpOk := i.(interp.RGBA) if dstOk && srcOk && interpOk { return a.transformRGBA(dstRGBA, srcRGBA, interpRGBA) } srcb := src.Bounds() b := dst.Bounds() for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { sx, sy := a.pt(x, y) if inBounds(srcb, sx, sy) { dst.Set(x, y, i.Interp(src, sx, sy)) } } } return nil }
func warm(out, in draw.Image, opacity uint8, colors []color.Color) { draw.Draw(out, out.Bounds(), image.Transparent, image.ZP, draw.Src) bounds := in.Bounds() collen := float64(len(colors)) wg := &sync.WaitGroup{} for x := bounds.Min.X; x < bounds.Max.X; x++ { wg.Add(1) go func(x int) { defer wg.Done() for y := bounds.Min.Y; y < bounds.Max.Y; y++ { col := in.At(x, y) _, _, _, alpha := col.RGBA() if alpha > 0 { percent := float64(alpha) / float64(0xffff) template := colors[int((collen-1)*(1.0-percent))] tr, tg, tb, ta := template.RGBA() ta /= 256 outalpha := uint8(float64(ta) * (float64(opacity) / 256.0)) outcol := color.NRGBA{ uint8(tr / 256), uint8(tg / 256), uint8(tb / 256), uint8(outalpha)} out.Set(x, y, outcol) } } }(x) } wg.Wait() }
func Bresenham(img draw.Image, color color.Color, x0, y0, x1, y1 int) { dx := abs(x1 - x0) dy := abs(y1 - y0) var sx, sy int if x0 < x1 { sx = 1 } else { sx = -1 } if y0 < y1 { sy = 1 } else { sy = -1 } err := dx - dy var e2 int for { img.Set(x0, y0, color) if x0 == x1 && y0 == y1 { return } e2 = 2 * err if e2 > -dy { err = err - dy x0 = x0 + sx } if e2 < dx { err = err + dx y0 = y0 + sy } } }
// 翻转函数,要求两个图像大小契合,act&1 == 0则左右翻转,否则垂直翻转。 func Overturn(dst draw.Image, src image.Image, act int) error { var to func(int, int) (int, int) sr := src.Bounds() dr := dst.Bounds() W := dr.Max.X - dr.Min.X H := dr.Max.Y - dr.Min.Y if H <= 0 || W <= 0 { return errors.New("target image is empty or noncanonical") } if sr.Min.X >= sr.Max.X || sr.Min.Y >= sr.Max.Y { return errors.New("source image is empty or noncanonical") } if sr.Max.X-sr.Min.X != W || sr.Max.Y-sr.Min.Y != H { return errors.New("target and source must be same size!") } if act&1 == 0 { to = func(x, y int) (int, int) { return W - 1 - x, y } } else { to = func(x, y int) (int, int) { return x, H - 1 - y } } for i := 0; i < W; i++ { for j := 0; j < H; j++ { x, y := to(i, j) dst.Set(dr.Min.X+x, dr.Min.Y+y, src.At(sr.Min.X+i, sr.Min.Y+j)) } } return nil }
// decodeP4 reads a binary bitmap func decodeP4(r *bufio.Reader, img draw.Image, width, height uint) { var x, y, bit int bytes := int(math.Ceil((float64(width) / 8))) bits := newBitset(uint(bytes) * height * 8) pad := (bytes * 8) - int(width) space(r) _, err := r.Read(bits) check(err) for y = 0; y < int(height); y++ { for x = 0; x < int(width); x++ { if bits.Test(bit) { img.Set(x, y, color.Alpha{0xff}) } else { img.Set(x, y, color.Alpha{0x00}) } bit++ } bit += pad } }
func gennoise(screen draw.Image) { for y := 0; y < 240; y++ { for x := 0; x < 320; x++ { screen.Set(x, y, <-randcol) } } }
func testDrawRandom(p draw.Image) { bd := p.Bounds() for y, yEnd := bd.Min.Y, bd.Max.Y; y < yEnd; y++ { for x, xEnd := bd.Min.X, bd.Max.X; x < xEnd; x++ { p.Set(x, y, testRandomColor()) } } }
func imageConvert(src image.Image, dest draw.Image) draw.Image { w, h := src.Bounds().Dx(), src.Bounds().Dy() for y := 0; y < h; y += 1 { for x := 0; x < w; x += 1 { dest.Set(x, y, dest.ColorModel().Convert(src.At(x, y))) } } return dest }
// Apply the given color mapping to the specified image buffers. func Apply(from, to *Rule, src, dst draw.Image) { var x, y int var r, g, b, a uint32 var sc color.Color var dc color.RGBA var pix pixel rect := src.Bounds() for y = 0; y < rect.Dy(); y++ { for x = 0; x < rect.Dx(); x++ { sc = src.At(x, y) r, g, b, a = sc.RGBA() pix.r = uint8(r >> 8) pix.g = uint8(g >> 8) pix.b = uint8(b >> 8) pix.a = uint8(a >> 8) // Check if the pixel matches the filter rule. if !(match(pix.r, from.R) && match(pix.g, from.G) && match(pix.b, from.B) && match(pix.a, from.A)) { dst.Set(x, y, sc) continue } // Compute three different types of grayscale conversion. // These can be applied by named references. pix.average = uint8(((r + g + b) / 3) >> 8) pix.lightness = uint8(((min(min(r, g), b) + max(max(r, g), b)) / 2) >> 8) // For luminosity it is necessary to apply an inverse of the gamma // function for the color space before calculating the inner product. // Then you apply the gamma function to the reduced value. Failure to // incorporate the gamma function can result in errors of up to 20%. // // For typical computer stuff, the color space is sRGB. The right // numbers for sRGB are approx. 0.21, 0.72, 0.07. Gamma for sRGB // is a composite function that approximates exponentiation by 1/2.2 // // This is a rather expensive operation, but gives a much more accurate // and satisfactory result than the average and lightness versions. pix.luminosity = gammaSRGB( 0.212655*invGammaSRGB(pix.r) + 0.715158*invGammaSRGB(pix.g) + 0.072187*invGammaSRGB(pix.b)) // Transform color. dc.R = transform(&pix, pix.r, to.R) dc.G = transform(&pix, pix.g, to.G) dc.B = transform(&pix, pix.b, to.B) dc.A = transform(&pix, pix.a, to.A) // Set new pixel. dst.Set(x, y, dc) } } }
// MapColorInRectangle is a helper function for working on part of an image. It // takes the original image, a function to use, a image to write to, and the // bounds of the original (and therefore the final image) to act upon. func MapColorInRectangle(img image.Image, bounds image.Rectangle, dest draw.Image, f Composable) { for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { dest.Set(x, y, f(img.At(x, y))) } } }
// decodeP1 reads an ASCII bitmap func decodeP1(r *bufio.Reader, img draw.Image, width, height uint) { var x, y int for y = 0; y < int(height); y++ { for x = 0; x < int(width); x++ { img.Set(x, y, color.Alpha{uint8(readU(r)) * 0xff}) } } }
func newSetFuncDefault(p draw.Image) SetFunc { return func(x, y int, r, g, b, a uint32) { p.Set(x, y, color.RGBA64{ R: uint16(r), G: uint16(g), B: uint16(b), A: uint16(a), }) } }
// Blends src image (top layer) into dst image (bottom layer) using // the BlendFunc provided by mode. BlendFunc is applied to each pixel // where the src image overlaps the dst image and the result is stored // in the original dst image, src image is unmutable. func BlendImage(dst draw.Image, src image.Image, mode BlendFunc) { // Obtain the intersection of both images. inter := dst.Bounds().Intersect(src.Bounds()) // Apply BlendFuc to each pixel in the intersection. for y := inter.Min.Y; y < inter.Max.Y; y++ { for x := inter.Min.X; x < inter.Max.X; x++ { dst.Set(x, y, mode(dst.At(x, y), src.At(x, y))) } } }
// DrawLinear draws a linear gradient to dst. If the gradient vector (as // defined by x0, y0, x1, and y1) is found to be purely horizontal or purely // vertical, the appropriate optimized functions will be called. func DrawLinear(dst draw.Image, x0, y0, x1, y1 float64, stops []Stop) { if y0 == y1 && x0 != x1 { drawHLinear(dst, x0, x1, stops) return } if x0 == x1 && y0 != y1 { drawVLinear(dst, y0, y1, stops) return } if len(stops) == 0 { return } if y0 > y1 { panic(fmt.Sprintf("invalid bounds y0(%f)>y1(%f)", y0, y1)) } if x0 > x1 { panic(fmt.Sprintf("invalid bounds x0(%f)>x1(%f)", x0, x1)) } bb := dst.Bounds() width, height := bb.Dx(), bb.Dy() x0, y0 = x0*float64(width), y0*float64(height) x1, y1 = x1*float64(width), y1*float64(height) dx, dy := x1-x0, y1-y0 px0, py0 := x0-dy, y0+dx mag := math.Hypot(dx, dy) var col color.Color for y := 0; y < width; y++ { fy := float64(y) for x := 0; x < width; x++ { fx := float64(x) // is the pixel before the start of the gradient? s0 := (px0-x0)*(fy-y0) - (py0-y0)*(fx-x0) if s0 > 0 { col = stops[0].Col } else { // calculate the distance of the pixel from the first stop line u := ((fx-x0)*(px0-x0) + (fy-y0)*(py0-y0)) / (mag * mag) x2, y2 := x0+u*(px0-x0), y0+u*(py0-y0) d := math.Hypot(fx-x2, fy-y2) / mag col = getColour(d, stops) } dst.Set(x+bb.Min.X, y+bb.Min.Y, col) } } }
func brush(img draw.Image, id int) func(x, y int) { colors := [][]uint8{ {0, 0, 0xff}, {0xff, 0, 0}, {0xff, 0xff, 0xff}, } c := colors[id%len(colors)] return func(x, y int) { img.Set(x, y, &color.NRGBA{R: c[0], G: c[1], B: c[2], A: 0xff}) } }
func (burkes) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) { quantError0 := make([][3]int32, r.Dx()+4) quantError1 := make([][3]int32, r.Dx()+4) out := color.RGBA64{A: 0xffff} for y := 0; y != r.Dy(); y++ { for x := 0; x != r.Dx(); x++ { sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA() er, eg, eb := int32(sr), int32(sg), int32(sb) er = clamp(er + quantError0[x+2][0]/32) eg = clamp(eg + quantError0[x+2][1]/32) eb = clamp(eb + quantError0[x+2][2]/32) out.R = uint16(er) out.G = uint16(eg) out.B = uint16(eb) dst.Set(r.Min.X+x, r.Min.Y+y, &out) sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() er -= int32(sr) eg -= int32(sg) eb -= int32(sb) quantError0[x+3][0] += er * 8 quantError0[x+3][1] += eg * 8 quantError0[x+3][2] += eb * 8 quantError0[x+4][0] += er * 4 quantError0[x+4][1] += eg * 4 quantError0[x+4][2] += eb * 4 quantError1[x+0][0] += er * 2 quantError1[x+0][1] += eg * 2 quantError1[x+0][2] += eb * 2 quantError1[x+1][0] += er * 4 quantError1[x+1][1] += eg * 4 quantError1[x+1][2] += eb * 4 quantError1[x+2][0] += er * 8 quantError1[x+2][1] += eg * 8 quantError1[x+2][2] += eb * 8 quantError1[x+3][0] += er * 4 quantError1[x+3][1] += eg * 4 quantError1[x+3][2] += eb * 4 quantError1[x+4][0] += er * 2 quantError1[x+4][1] += eg * 2 quantError1[x+4][2] += eb * 2 } // Recycle the quantization error buffers. quantError0, quantError1 = quantError1, quantError0 for i := range quantError1 { quantError1[i] = [3]int32{} } } }
func (hs *AutoSlicer) drawRegion(img draw.Image, r Region) { var ( c color.Color x, y int ) c = color.RGBA{0xFF, 0, 0, 0xFF} for x = r.Min.X + 1; x < r.Max.X; x++ { for y = r.Min.Y + 1; y < r.Max.Y; y++ { img.Set(x, y, c) } } }
// decodeP2 reads an ASCII graymap func decodeP2(r *bufio.Reader, img draw.Image, width, height uint) { var x, y int mask := readU(r) mul := 255 / mask for y = 0; y < int(height); y++ { for x = 0; x < int(width); x++ { img.Set(x, y, color.Gray{uint8((readU(r) & mask) * mul)}) } } }
func noise(src draw.Image) { orig := src.Bounds() numToMod := (orig.Max.X * orig.Max.Y) / 2 for i := 0; i < numToMod; i++ { x := rand.Intn(orig.Max.X) y := rand.Intn(orig.Max.Y) origColor := src.At(x, y).(color.RGBA) origColor.R += 30 origColor.B += 30 origColor.G += 30 src.Set(x, y, origColor) } }
func DrawDigit(dest draw.Image, dig digitmap, sx int, sy int) { c := 0 for y, h := sy, 0; h < 7; y, h = y+1, h+1 { for x, w := sx, 0; w < 4; x, w = x+1, w+1 { if dig[c] > 0 { dest.Set(x, y, color.White) } else { dest.Set(x, y, color.Black) } c += 1 } } }
// 旋转,每单位1代表左旋90度。注意采用的是向右向上的直角坐标系,和图像的向右向下不同。 // 本包的函数都是采用向右向上的直角坐标系。因此在图像显示上,每单位1右旋90度。 func Rotate(dst draw.Image, src image.Image, act int) error { var to func(int, int) (int, int) sr := src.Bounds() dr := dst.Bounds() W := dr.Max.X - dr.Min.X H := dr.Max.Y - dr.Min.Y if H <= 0 || W <= 0 { return errors.New("target image is empty or noncanonical") } if sr.Min.X >= sr.Max.X || sr.Min.Y >= sr.Max.Y { return errors.New("source image is empty or noncanonical") } switch act %= 4; act { case 2, -2: if sr.Max.X-sr.Min.X != W || sr.Max.Y-sr.Min.Y != H { return errors.New("target and source must be same size!") } to = func(x, y int) (int, int) { return W - 1 - x, H - 1 - y } case 1, -3: if sr.Max.X-sr.Min.X != H || sr.Max.Y-sr.Min.Y != W { return errors.New("target and source must be same size!") } to = func(x, y int) (int, int) { return H - 1 - y, x } case -1, 3: if sr.Max.X-sr.Min.X != H || sr.Max.Y-sr.Min.Y != W { return errors.New("target and source must be same size!") } to = func(x, y int) (int, int) { return y, W - 1 - x } case 0: if sr.Max.X-sr.Min.X != W || sr.Max.Y-sr.Min.Y != H { return errors.New("target and source must be same size!") } to = func(x, y int) (int, int) { return x, y } } for i := 0; i < W; i++ { for j := 0; j < H; j++ { x, y := to(i, j) dst.Set(dr.Min.X+x, dr.Min.Y+y, src.At(sr.Min.X+i, sr.Min.Y+j)) } } return nil }
func digit(im draw.Image, col color.Color, x, y, size, digit int) { n := hexdigits[digit] for _, s := range segments { if n&1 != 0 { xx, yy := x+s.sx*size, y+s.sy*size for i := 0; i <= size; i++ { im.Set(xx, yy, col) xx += s.dx yy += s.dy } } n >>= 1 } }
/* Line drawing - Bresenhams algorithm in it's simplest form.*/ func DrawLine(screen draw.Image, a image.Point, b image.Point, color color.Color) { dx := b.X - a.X dy := b.Y - a.Y error := 0.0 derr := math.Abs(float64(dy) / float64(dx)) y := a.Y for x := a.X; x < b.X; x++ { screen.Set(x, y, color) error += derr if error >= 0.5 { y++ error -= 1.0 } } }
func DrawColon(dest draw.Image, sx int, sy int) { c := 0 for y, h := sy, 0; h < 7; y, h = y+1, h+1 { for x, w := sx, 0; w < 2; x, w = x+1, w+1 { if colonslice[c] > 0 { dest.Set(x, y, color.White) } else { dest.Set(x, y, color.Black) } c += 1 } } }
func FillArc(horizontal, vertical int, imagem draw.Image, cor color.Color) { var passo int if vertical > 0 { passo = 1 } else { passo = -1 } for indice := 0; indice != vertical; indice += passo { imagem.Set(horizontal, indice, cor) imagem.Set(indice, horizontal, cor) } imagem.Set(horizontal, vertical, cor) imagem.Set(vertical, horizontal, cor) }
func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) { bounds := src.Bounds() x0, y0, x1, y1 := float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y) tr.TransformRectangle(&x0, &y0, &x1, &y1) var x, y, u, v float64 var c1, c2, cr color.Color var r, g, b, a, ia, r1, g1, b1, a1, r2, g2, b2, a2 uint32 var color color.RGBA for x = x0; x < x1; x++ { for y = y0; y < y1; y++ { u = x v = y tr.InverseTransform(&u, &v) if bounds.Min.X <= int(u) && bounds.Max.X > int(u) && bounds.Min.Y <= int(v) && bounds.Max.Y > int(v) { c1 = dest.At(int(x), int(y)) switch filter { case LinearFilter: c2 = src.At(int(u), int(v)) case BilinearFilter: c2 = getColorBilinear(src, u, v) case BicubicFilter: c2 = getColorBicubic(src, u, v) } switch op { case draw.Over: r1, g1, b1, a1 = c1.RGBA() r2, g2, b2, a2 = c2.RGBA() ia = M - a2 r = ((r1 * ia) / M) + r2 g = ((g1 * ia) / M) + g2 b = ((b1 * ia) / M) + b2 a = ((a1 * ia) / M) + a2 color.R = uint8(r >> 8) color.G = uint8(g >> 8) color.B = uint8(b >> 8) color.A = uint8(a >> 8) cr = color default: cr = c2 } dest.Set(int(x), int(y), cr) } } } }