// Draw renders the given cpu cores on img. func (app *App) Draw(img draw.Image, cpus []CPU) { rect := img.Bounds() bg := app.Background if bg == nil { bg = image.Black } draw.Draw(img, rect, bg, bg.Bounds().Min, draw.Over) if len(cpus) == 0 { return } cpuDx := rect.Dx() / len(cpus) ptIncr := image.Point{X: cpuDx} ptDelta := image.Point{} rectDx := image.Rectangle{ Min: rect.Min, Max: rect.Max, } rectDx.Max.X = rect.Min.X + cpuDx for _, cpu := range cpus { irect := image.Rectangle{ Min: rectDx.Min.Add(ptDelta), Max: rectDx.Max.Add(ptDelta), } subimg := SubImage(img, irect) app.renderCPU(subimg, cpu) ptDelta = ptDelta.Add(ptIncr) } }
// 翻转函数,要求两个图像大小契合,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 }
func (ff *FontFace) GetImage(text string) (img draw.Image, err error) { var ( src image.Image bg image.Image dst draw.Image pt fixed.Point26_6 w int h int ) src = image.NewUniform(ff.fg) bg = image.NewUniform(ff.bg) w = int(float32(len(text)) * ff.charw) h = int(ff.charh) dst = image.NewRGBA(image.Rect(0, 0, w, h)) draw.Draw(dst, dst.Bounds(), bg, image.ZP, draw.Src) ff.context.SetSrc(src) ff.context.SetDst(dst) ff.context.SetClip(dst.Bounds()) pt = freetype.Pt(0, int(ff.charh+ff.offy)) if pt, err = ff.context.DrawString(text, pt); err != nil { return } img = image.NewRGBA(image.Rect(0, 0, int(pt.X/64), int(pt.Y/64))) draw.Draw(img, img.Bounds(), dst, image.Pt(0, -int(ff.offy)), draw.Src) return }
func (p *Drawer) PasteAt(img draw.Image, pt P) { flr := pt.Floor() bounds := img.Bounds() dp := image.Point{flr[0], flr[1]} rec := bounds.Sub(bounds.Min).Add(dp) draw.Draw(p.Img, rec, img, bounds.Min, draw.Over) }
// 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() }
// Thumbnail scales and crops src so it fits in dst. func Thumbnail(dst draw.Image, src image.Image) error { // Scale down src in the dimension that is closer to dst. sb := src.Bounds() db := dst.Bounds() rx := float64(sb.Dx()) / float64(db.Dx()) ry := float64(sb.Dy()) / float64(db.Dy()) var b image.Rectangle if rx < ry { b = image.Rect(0, 0, db.Dx(), int(float64(sb.Dy())/rx)) } else { b = image.Rect(0, 0, int(float64(sb.Dx())/ry), db.Dy()) } buf := image.NewRGBA(b) if err := Scale(buf, src); err != nil { return err } // Crop. // TODO(crawshaw): improve on center-alignment. var pt image.Point if rx < ry { pt.Y = (b.Dy() - db.Dy()) / 2 } else { pt.X = (b.Dx() - db.Dx()) / 2 } draw.Draw(dst, db, buf, pt, draw.Src) return nil }
// NewImage returns a new image canvas // that draws to the given image. The // minimum point of the given image // should probably be 0,0. func NewImage(img draw.Image, name string) (*Canvas, error) { w := float64(img.Bounds().Max.X - img.Bounds().Min.X) h := float64(img.Bounds().Max.Y - img.Bounds().Min.Y) X, err := xgbutil.NewConn() if err != nil { return nil, err } keybind.Initialize(X) ximg := xgraphics.New(X, image.Rect(0, 0, int(w), int(h))) err = ximg.CreatePixmap() if err != nil { return nil, err } painter := NewPainter(ximg) gc := draw2d.NewGraphicContextWithPainter(ximg, painter) gc.SetDPI(dpi) gc.Scale(1, -1) gc.Translate(0, -h) wid := ximg.XShowExtra(name, true) go func() { xevent.Main(X) }() c := &Canvas{ Canvas: vgimg.NewWith(vgimg.UseImageWithContext(img, gc)), x: X, ximg: ximg, wid: wid, } vg.Initialize(c) return c, nil }
// Convolve produces dst by applying the convolution kernel k to src. func Convolve(dst draw.Image, src image.Image, k Kernel) (err error) { if dst == nil || src == nil || k == nil { return nil } b := dst.Bounds() dstRgba, ok := dst.(*image.RGBA) if !ok { dstRgba = image.NewRGBA(b) } switch k := k.(type) { case *SeparableKernel: err = convolveRGBASep(dstRgba, src, k) default: err = convolveRGBA(dstRgba, src, k) } if err != nil { return err } if !ok { draw.Draw(dst, b, dstRgba, b.Min, draw.Src) } return nil }
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() }
// NewFromImage uses the given image as the destination for all calls to Add. // It is assumed to be empty at the beginning so all the available space will be // used for sub-images. func NewFromImage(atlas draw.Image) *Atlas { packer := binpacker.New(atlas.Bounds().Dx(), atlas.Bounds().Dy()) return &Atlas{ Image: atlas, packer: packer, } }
// RenderCPU implements the Renderer interface. func (b *Border) RenderCPU(img draw.Image, cpu CPU) { rect := img.Bounds() interior := geometry.Contract(rect, b.Size) mask := MaskInside(interior) draw.DrawMask(img, rect, image.NewUniform(b.Color), image.ZP, mask, rect.Min, draw.Over) sub := SubImage(img, interior) b.Renderer.RenderCPU(sub, cpu) }
func newImageDimentions(img draw.Image, angle float64) (int, int) { bounds := img.Bounds() width := float64(bounds.Max.X - bounds.Min.X) height := float64(bounds.Max.Y - bounds.Min.Y) w, h := newDimentions(width, height, angle) return int(w), int(h) }
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()) } } }
// 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) } } }
// 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))) } } }
// Like PixelSizeChanger, but instead of the pixel size the new image // is given. func DimensionChanger(img draw.Image, w, h int) draw.Image { b := img.Bounds().Canon() return &dimensionChanger{ Image: img, paddingX: b.Dx() % w, paddingY: b.Dy() % h, pixel: image.Rect(0, 0, b.Dx()/w, b.Dy()/h), bounds: image.Rect(0, 0, w, h), } }
// TransformCenter applies the affine transform to src and produces dst. // Equivalent to // a.CenterFit(dst, src).Transform(dst, src, i). func (a Affine) TransformCenter(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") } return a.CenterFit(dst.Bounds(), src.Bounds()).Transform(dst, src, i) }
// NewBackground creates a new Background object that // draws to img, and draws the actual background with bg. // The flush function, if non-nil, will be called to // whenever changes are to be made visible externally // (for example when Flush() is called. // // Note that bg is drawn with the draw.Src operation, // so it is possible to create images with a transparent // background. // func NewBackground(img draw.Image, bg image.Image, flush func(r image.Rectangle)) *Background { r := img.Bounds() return &Background{ img: img, bg: bg, r: r, flushrect: r, imgflush: flush, } }
// SliceImage returns an image which is a view onto a portion of img. // The returned image has the specified width and height, // but all draw operations are clipped to r. // The origin of img is aligned with p. Where img // overlaps with r, it will be used for drawing operations. // func SliceImage(width, height int, r image.Rectangle, img draw.Image, p image.Point) draw.Image { // TODO: detect when img is itself a SliceImage and // use the underlying image directly. i := new(imageSlice) i.img = img i.r = r.Intersect(image.Rectangle{p, p.Add(img.Bounds().Size())}) //debugp("actual sliced rectangle %v\n", i.r) i.p = p return i }
// 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) } } }
// RenderCPU implements the Renderer interface. func (frac *FractionRenderer) RenderCPU(img draw.Image, cpu CPU) { rect := img.Bounds() utilized := cpu.FracUtil() utilizedHeight := int(float64(rect.Dy()) * utilized) yoffset := rect.Dy() - utilizedHeight rect.Min = rect.Min.Add(image.Pt(0, yoffset)) img = SubImage(img, rect) frac.Renderer.RenderCPU(img, cpu) }
func (p *Drawer) NoAddMerge(img draw.Image) { dest := p.Img srcBounds := img.Bounds() destB := dest.Bounds() destRect := srcBounds.Sub(srcBounds.Min).Add(destB.Min) source := img sourcePt := srcBounds.Min maskPt := destRect.Min mask := p.Img draw.DrawMask(dest, destRect, source, sourcePt, mask, maskPt, draw.Over) }
func (l limits) translate(p DataPoint, i draw.Image, dotsize int) (rv image.Point) { // Normalize to 0-1 x := float64(p.X()-l.Min.X()) / float64(l.Dx()) y := float64(p.Y()-l.Min.Y()) / float64(l.Dy()) // And remap to the image rv.X = int(x * float64((i.Bounds().Max.X - dotsize))) rv.Y = int((1.0 - y) * float64((i.Bounds().Max.Y - dotsize))) return }
func drawImg(dst draw.Image, asset string, err error) error { if err != nil { return err } src, _, err := image.Decode(bytes.NewReader(MustAsset(asset))) if err != nil { return err } draw.Draw(dst, dst.Bounds(), src, image.Point{0, 0}, draw.Over) return nil }
// clip clips r against each image's bounds (after translating into // the destination image's co-ordinate space) and shifts the point // sp by the same amount as the change in r.Min. func clip(dst draw.Image, r *image.Rectangle, src image.Image, sp *image.Point) { orig := r.Min *r = r.Intersect(dst.Bounds()) *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp))) dx := r.Min.X - orig.X dy := r.Min.Y - orig.Y if dx == 0 && dy == 0 { return } (*sp).X += dx (*sp).Y += dy }
// Copy copies src to dst. func Copy(dst draw.Image, src image.Image) { bd := src.Bounds().Intersect(dst.Bounds()) at := NewAtFunc(src) set := NewSetFunc(dst) Parallel(bd, func(bd image.Rectangle) { for y := bd.Min.Y; y < bd.Max.Y; y++ { for x := bd.Min.X; x < bd.Max.X; x++ { r, g, b, a := at(x, y) set(x, y, r, g, b, a) } } }) }
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) } }
// NewGraphicContextWithPainter creates a new Graphic context from an image and a Painter (see Freetype-go) func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphicContext { width, height := img.Bounds().Dx(), img.Bounds().Dy() dpi := 92 gc := &ImageGraphicContext{ NewStackGraphicContext(), img, painter, raster.NewRasterizer(width, height), raster.NewRasterizer(width, height), truetype.NewGlyphBuf(), dpi, } return gc }
// SubImage extracts a rectangular subset of img as a separate image. // If present, the `SubImage()` method will be used. Otherwise, the // behaviour will be emulated. func SubImage(img draw.Image, r image.Rectangle) draw.Image { if di, ok := img.(subimager); ok { si := di.SubImage(r) dsi, ok := si.(draw.Image) if !ok { panic("Image is drawable, subimage is not.") } return dsi } return &subimage{ Image: img, bounds: img.Bounds().Intersect(r), } }