// imgHandle serves images of the player's view. func imgHandle(w http.ResponseWriter, r *http.Request) { quality, err := strconv.Atoi(r.FormValue("quality")) if err != nil || quality < 0 || quality > 100 { quality = 70 } // Center Gopher in view if possible gpos := model.Gopher.Pos rect := image.Rect(0, 0, ViewWidth, ViewHeight).Add(image.Pt(int(gpos.X)-ViewWidth/2, int(gpos.Y)-ViewHeight/2)) // But needs correction at the edges of the view (it can't be centered) corr := image.Point{} if rect.Min.X < 0 { corr.X = -rect.Min.X } if rect.Min.Y < 0 { corr.Y = -rect.Min.Y } if rect.Max.X > model.LabWidth { corr.X = model.LabWidth - rect.Max.X } if rect.Max.Y > model.LabHeight { corr.Y = model.LabHeight - rect.Max.Y } rect = rect.Add(corr) model.Mutex.Lock() jpeg.Encode(w, model.LabImg.SubImage(rect), &jpeg.Options{quality}) model.Mutex.Unlock() // Store the new view's position: Pos = rect.Min }
func (f *Frame) _draw(pt image.Point) image.Point { for nb := 0; nb < f.nbox; nb++ { b := f.box[nb] f.cklinewrap0(&pt, b) if pt.Y == f.Rect.Max.Y { f.nchars -= f.strlen(nb) f.delbox(nb, f.nbox-1) break } if b.Nrune > 0 { n, fits := f.canfit(pt, b) if !fits { break } if n != b.Nrune { f.splitbox(uint64(nb), uint64(n)) b = f.box[nb] } pt.X += b.Wid } else { if b.Bc == '\n' { pt.X = f.Rect.Min.X pt.Y += f.Font.Height } else { pt.X += f.newwid(pt, b) } } } return pt }
func (self *Image) sourceRectTouchOriginalFromInside(width, height int, horAlign HorAlignment, verAlign VerAlignment) (r image.Rectangle) { var offset image.Point aspectRatio := float64(width) / float64(height) if aspectRatio > self.AspectRatio() { // Wider than original // so touchOriginalFromInside means // that the source rect is as wide as the original r.Max.X = self.Width() r.Max.Y = int(float64(self.Width()) / aspectRatio) switch verAlign { case VerCenter: offset.Y = (self.Height() - r.Max.Y) / 2 case Bottom: offset.Y = self.Height() - r.Max.Y } } else { // Heigher than original, // so touchOriginalFromInside means // that the source rect is as high as the original r.Max.X = int(float64(self.Height()) * aspectRatio) r.Max.Y = self.Height() switch horAlign { case HorCenter: offset.X = (self.Width() - r.Max.X) / 2 case Right: offset.X = self.Width() - r.Max.X } } return r.Add(offset) }
// originTrans translates the origin with respect to the current image and the // current canvas size. This makes sure we never incorrect position the image. // (i.e., panning never goes too far, and whenever the canvas is bigger than // the image, the origin is *always* (0, 0). func originTrans(pt image.Point, win *window, img *vimage) image.Point { // If there's no valid image, then always return (0, 0). if img == nil { return image.Point{0, 0} } // Quick aliases. ww, wh := win.Geom.Width(), win.Geom.Height() dw := img.Bounds().Dx() - ww dh := img.Bounds().Dy() - wh // Set the allowable range of the origin point of the image. // i.e., never less than (0, 0) and never greater than the width/height // of the image that isn't viewable at any given point (which is determined // by the canvas size). pt.X = min(img.Bounds().Min.X+dw, max(pt.X, 0)) pt.Y = min(img.Bounds().Min.Y+dh, max(pt.Y, 0)) // Validate origin point. If the width/height of an image is smaller than // the canvas width/height, then the image origin cannot change in x/y // direction. if img.Bounds().Dx() < ww { pt.X = 0 } if img.Bounds().Dy() < wh { pt.Y = 0 } return pt }
func (f *Frame) advance(p *image.Point, b *frbox) { if b.Nrune < 0 && b.Bc == '\n' { p.X = f.Rect.Min.X p.Y += f.Font.Height } else { p.X += b.Wid } }
func main() { g = image.NewGray(image.Rectangle{image.Point{0, 0}, image.Point{w, h}}) // off center seed position makes pleasingly asymetrical tree g.SetGray(w/3, h/3, color.Gray{frost}) generate: for a := 0; a < n; { // generate random position for new particle rp := image.Point{rand.Intn(w), rand.Intn(h)} if g.At(rp.X, rp.Y).(color.Gray).Y == frost { // position is already set. find a nearby free position. for { rp.X += rand.Intn(3) - 1 rp.Y += rand.Intn(3) - 1 // execpt if we run out of bounds, consider the particle lost. if !rp.In(g.Rect) { continue generate } if g.At(rp.X, rp.Y).(color.Gray).Y != frost { break } } } else { // else particle is in free space. let it wander // until it touches tree for !hasNeighbor(rp) { rp.X += rand.Intn(3) - 1 rp.Y += rand.Intn(3) - 1 // but again, if it wanders out of bounds consider it lost. if !rp.In(g.Rect) { continue generate } } } // x, y now specify a free position toucing the tree. g.SetGray(rp.X, rp.Y, color.Gray{frost}) a++ // progress indicator if a%100 == 0 { fmt.Println(a, "of", n) } } f, err := os.Create("tree.png") if err != nil { fmt.Println(err) return } err = png.Encode(f, g) if err != nil { fmt.Println(err) } f.Close() }
func (f *Frame) cklinewrap(p *image.Point, b *frbox) { if b.Nrune < 0 { if b.Minwid > byte(f.Rect.Max.X-p.X) { p.X = f.Rect.Min.X p.Y += f.Font.Height } } else { if b.Wid > f.Rect.Max.X-p.X { p.X = f.Rect.Min.X p.Y += f.Font.Height } } }
func (atlas *ManagedAtlas) LoadGroupSheetOffset(path string, pt image.Point, width, height, frames int) (err error, groupID ID) { fName := filepath.Base(path) extIndex := strings.LastIndex(fName, ".") if extIndex != -1 { fName = fName[:extIndex] } fName = atlas.nextGroupID(fName) file, e := os.Open(path) if e != nil { return e, nil } defer file.Close() ds, e := file.Stat() if e != nil { return e, nil } if ds.IsDir() { return errors.New("The path is not a file. " + path), nil } file.Close() img, e := LoadImage(path) if e != nil { return e, nil } group := make([]ID, 0) point := image.Point{pt.X, pt.Y} for i := 0; i < frames; i++ { is := strconv.FormatInt(int64(i), 10) if i == 0 { is = "" } sprite := image.NewRGBA(image.Rect(0, 0, width, height)) draw.Draw(sprite, sprite.Rect, img, point, draw.Src) atlas.AddImage(sprite, fName+is) group = append(group, fName+is) point.X += width if !sprite.Rect.Add(point).In(img.Bounds()) { point.X = 0 point.Y += height } } atlas.groups[fName] = group return nil, fName }
func collide(pt image.Point, p *Piece) bool { pt.X = (pt.X - rboard.Min.X) / pcsz pt.Y = (pt.Y - rboard.Min.Y) / pcsz for _, q := range p.d { pt.X += q.X pt.Y += q.Y if pt.X < 0 || pt.X >= NX || pt.Y < 0 || pt.Y >= NY { return true } if board[pt.Y][pt.X] != 0 { return true } } return false }
// Clamp a bound to inside the given bounds func clamp(point image.Point, bounds image.Rectangle) image.Point { if point.X < bounds.Min.X { point.X = bounds.Min.X } if point.X >= bounds.Max.X { point.X -= bounds.Max.X - 1 } if point.Y < bounds.Min.Y { point.Y = bounds.Min.Y } if point.Y >= bounds.Max.Y { point.Y = bounds.Max.Y - 1 } return point }
func anchor(r image.Rectangle, flags Anchor, p image.Point) image.Rectangle { var dp image.Point switch flags & (E | W) { case E: dp.X = r.Dx() case E | W, 0: dp.X = r.Dx() / 2 } switch flags & (N | S) { case S: dp.Y = r.Dy() case S | N, 0: dp.Y = r.Dy() / 2 } return r.Add(p.Sub(r.Min).Sub(dp)) }
func canfit(p *Piece) bool { var dx = [...]int{0, -1, 1, -2, 2, -3, 3, 4, -4} j := N + 1 if j >= 4 { j = p.sz.X if j < p.sz.Y { j = p.sz.Y } j = 2*j - 1 } for i := 0; i < j; i++ { var z image.Point z.X = pos.X + dx[i]*pcsz z.Y = pos.Y if !collide(z, p) { z.Y = pos.Y + pcsz - 1 if !collide(z, p) { undrawpiece() pos.X = z.X return true } } } return false }
func main() { bounds := image.Rect(0, 0, 100, 100) im := image.NewGray(bounds) gBlack := color.Gray{0} gWhite := color.Gray{255} draw.Draw(im, bounds, image.NewUniform(gWhite), image.ZP, draw.Src) pos := image.Point{50, 50} dir := up for pos.In(bounds) { switch im.At(pos.X, pos.Y).(color.Gray).Y { case gBlack.Y: im.SetGray(pos.X, pos.Y, gWhite) dir-- case gWhite.Y: im.SetGray(pos.X, pos.Y, gBlack) dir++ } if dir&1 == 1 { pos.X += 1 - dir&2 } else { pos.Y -= 1 - dir&2 } } f, err := os.Create("ant.png") if err != nil { fmt.Println(err) return } if err = png.Encode(f, im); err != nil { fmt.Println(err) } if err = f.Close(); err != nil { fmt.Println(err) } }
func (f *Frame) ptofcharptb(p uint64, pt image.Point, bn int) image.Point { var b *frbox var w int var r rune for ; bn < f.nbox; bn++ { b = f.box[bn] f.cklinewrap(&pt, b) l := nrune(b) if p < uint64(l) { if b.Nrune > 0 { for s := 0; s < len(b.Ptr) && p > 0; s += w { p-- r, w = utf8.DecodeRune(b.Ptr[s:]) pt.X += f.Font.StringWidth(string(b.Ptr[s : s+1])) if r == 0 || pt.X > f.Rect.Max.X { panic("frptofchar") } } } break } p -= uint64(l) f.advance(&pt, b) } return pt }
func (f *Frame) SelectPaint(p0, p1 image.Point, col *draw.Image) { q0 := p0 q1 := p1 q0.Y += f.Font.Height q1.Y += f.Font.Height n := (p1.Y - p0.Y) / f.Font.Height if f.Background == nil { panic("Frame.SelectPaint B == nil") } if p0.Y == f.Rect.Max.Y { return } if n == 0 { f.Background.Draw(Rpt(p0, q1), col, nil, image.ZP) } else { if p0.X >= f.Rect.Max.X { p0.X = f.Rect.Max.X - 1 } f.Background.Draw(image.Rect(p0.X, p0.Y, f.Rect.Max.X, q0.Y), col, nil, image.ZP) if n > 1 { f.Background.Draw(image.Rect(f.Rect.Min.X, q0.Y, f.Rect.Max.X, p1.Y), col, nil, image.ZP) } f.Background.Draw(image.Rect(f.Rect.Min.X, p1.Y, q1.X, q1.Y), col, nil, image.ZP) } }
// 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 }
func (f *Frame) grid(p image.Point) image.Point { p.Y -= f.Rect.Min.Y p.Y -= p.Y % f.Font.Height p.Y += f.Rect.Min.Y if p.X > f.Rect.Max.X { p.X = f.Rect.Max.X } return p }
// clip clips r against each image's bounds (after translating into the // destination image's coordinate space) and shifts the points sp and mp by // the same amount as the change in r.Min. func clip(dst draw.Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) { orig := r.Min *r = r.Intersect(dst.Bounds()) *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp))) if mask != nil { *r = r.Intersect(mask.Bounds().Add(orig.Sub(*mp))) } dx := r.Min.X - orig.X dy := r.Min.Y - orig.Y if dx == 0 && dy == 0 { return } sp.X += dx sp.Y += dy if mp != nil { mp.X += dx mp.Y += dy } }
func GenerateImage(generatorSpecifications GeneratorSpecifications) *image.RGBA { minPoint := image.Point{0, 0} maxPoint := minPoint for i := 0; i < len(generatorSpecifications.lines); i++ { currentLen := len(generatorSpecifications.lines[i].points) for j := 1; j <= currentLen; j++ { currentPoint := Point(generatorSpecifications.lines[i].points[j%currentLen]) max := Max(int(generatorSpecifications.lines[i].jagged[j%currentLen]), int(generatorSpecifications.lines[i].jagged[j-1])) + int(generatorSpecifications.lines[i].width) min := max / 2 max -= min maxPoint.X = Max(maxPoint.X, currentPoint.X+max) maxPoint.Y = Max(maxPoint.Y, currentPoint.Y+max) minPoint.X = Min(maxPoint.X, currentPoint.X-min) minPoint.Y = Min(maxPoint.Y, currentPoint.Y-min) } } for i := 0; i < len(generatorSpecifications.circles); i++ { currentPoint := Point(generatorSpecifications.circles[i].point) max := int(generatorSpecifications.circles[i].width + generatorSpecifications.circles[i].jagged) min := max / 2 max += int(generatorSpecifications.circles[i].radius) - min min += int(generatorSpecifications.circles[i].radius) maxPoint.X = Max(maxPoint.X, currentPoint.X+max) maxPoint.Y = Max(maxPoint.Y, currentPoint.Y+max) minPoint.X = Min(maxPoint.X, currentPoint.X-min) minPoint.Y = Min(maxPoint.Y, currentPoint.Y-min) } ret := image.NewRGBA(image.Rectangle{minPoint, maxPoint}) for i := 0; i < len(generatorSpecifications.lines); i++ { DrawLine(ret, generatorSpecifications.lines[i]) } for i := 0; i < len(generatorSpecifications.circles); i++ { DrawCircle(ret, generatorSpecifications.circles[i]) } return ret }
func (f *Frame) DrawText(pt image.Point, text *draw.Image, back *draw.Image) { for nb := 0; nb < f.nbox; nb++ { b := f.box[nb] f.cklinewrap(&pt, b) if !f.noredraw && b.Nrune >= 0 { f.Background.String(pt, text, image.ZP, f.Font, string(b.Ptr)) } pt.X += b.Wid } }
func (poly *jsonWedPolygon) BoundingBox() image.Rectangle { min := image.Point{math.MaxUint16, math.MaxUint16} max := image.Point{0, 0} for _, vert := range poly.Verts { if vert.X < min.X { min.X = int(vert.X) } if vert.Y < min.Y { min.Y = int(vert.Y) } if vert.X > max.X { max.X = int(vert.X) } if vert.Y > max.Y { max.Y = int(vert.Y) } } return image.Rectangle{min, max}.Canon() }
func fitted(into image.Rectangle, size image.Point) image.Rectangle { ratio := float64(size.X) / float64(size.Y) if size.X > into.Dx() { size.X = into.Dx() size.Y = int(float64(into.Dx()) / ratio) } if size.Y > into.Dy() { size.X = int(float64(into.Dy()) * ratio) size.Y = into.Dy() } r := image.Rectangle{ Min: into.Min, Max: into.Min.Add(size), } r = r.Add(image.Point{into.Dx() / 2, into.Dy() / 2}). Sub(image.Point{size.X / 2, size.Y / 2}) return r }
func getSpriteSize(imgs []*cssImage) image.Point { p := image.Point{} for _, img := range imgs { st := img.img if st.sp.X == -1 { st.sp = image.Pt(-p.X, 0) p.X += st.dx() if p.Y < st.dy() { p.Y = st.dy() } } } return p }
// Image Handler // Reads the stored GIF & overlay for the authenticated user and returns a new GIF with the overlay copied onto each frame of the input GIF func imageHandler(w http.ResponseWriter, r *http.Request) { token, _ := jwt.ParseFromRequest(r, hmacKeyFunc) issuer := token.Claims["iss"].(string) gfFile, err1 := os.Open(getFileName("gif:" + issuer)) overlayFile, err2 := os.Open(getFileName("overlay:" + issuer)) if err1 != nil || err2 != nil { sendResponse(w, 400, map[string]string{ "error": "Error while reading images", }) return } // Decode images dst, err1 := gif.DecodeAll(gfFile) src, _, err2 := image.Decode(overlayFile) if err1 != nil || err2 != nil { sendResponse(w, 400, map[string]string{ "error": "Error while decoding images", }) return } // Create location for the draw pt := new(image.Point) x, err := strconv.Atoi(r.URL.Query().Get("x")) if err != nil { x = 0 } y, err := strconv.Atoi(r.URL.Query().Get("y")) if err != nil { y = 0 } pt.X = x pt.Y = y // Draw the overlay over each frame of the GIF for _, frame := range dst.Image { draw.Draw(frame, frame.Bounds(), src, *pt, draw.Over) } // Encode image to base64 buffer := new(bytes.Buffer) gif.EncodeAll(buffer, dst) fmt.Fprint(w, base64.StdEncoding.EncodeToString(buffer.Bytes())) }
func (f *Frame) newwid0(pt image.Point, b *frbox) int { c := f.Rect.Max.X x := pt.X if b.Nrune >= 0 || b.Bc != '\t' { return b.Wid } if x+int(b.Minwid) > c { pt.X = f.Rect.Min.X x = pt.X } x += f.maxtab x -= (x - f.Rect.Min.X) % f.maxtab if x-pt.X < int(b.Minwid) || x > c { x = pt.X + int(b.Minwid) } return x - pt.X }
func (poly *jsonWedPolygon) EffectedTiles(pt image.Point) []int { out := make([]int, 0) bb := poly.BoundingBox() bb.Min.X /= 64 bb.Min.Y /= 64 bb.Max.X /= 64 bb.Max.Y /= 64 pt.X /= 64 pt.Y /= 64 // All tiles in our bounding box should be considered effected for y := bb.Min.Y; y <= bb.Max.Y; y++ { for x := bb.Min.X; x <= bb.Max.X; x++ { out = append(out, y*pt.X+x) } } return out }
func (f *Frame) _tick(pt image.Point, ticked bool) { if f.ticked == ticked || f.tick == nil || !pt.In(f.Rect) { return } pt.X -= f.tickscale r := image.Rect(pt.X, pt.Y, pt.X+frtickw*f.tickscale, pt.Y+f.Font.Height) if r.Max.X > f.Rect.Max.X { r.Max.X = f.Rect.Max.X } if ticked { f.tickback.Draw(f.tickback.R, f.Background, nil, pt) f.Background.Draw(r, f.tick, nil, image.ZP) } else { f.Background.Draw(r, f.tickback, nil, image.ZP) } f.ticked = ticked }
// Uses half-sample symmetry. func Symmetric(im image.Image, p image.Point) color.Color { b := im.Bounds() // Make twice as big. d := image.Rectangle{b.Min, b.Max.Add(b.Size())} p = p.Mod(d) // Move to origin. p = p.Sub(b.Min) w, h := b.Dx(), b.Dy() if p.X > w-1 { p.X = 2*w - 1 - p.X } if p.Y > h-1 { p.Y = 2*h - 1 - p.Y } p = p.Add(b.Min) return im.At(p.X, p.Y) }
// Warning: does modify contents of polys func DrawPolygons(mul float64, polys []polyclip.Polygon) *image.NRGBA { img := image.NewNRGBA(image.Rect(0, 0, 0, 0)) min := polyclip.Point{0, 0} for i, polygon := range polys { r0 := safebbox(polygon) translate := image.Point{} if r0.Min.X < min.X { translate.X = int(mul*min.X) - int(mul*r0.Min.X) min.X = r0.Min.X } if r0.Min.Y < min.Y { translate.Y = int(mul*min.Y) - int(mul*r0.Min.Y) min.Y = r0.Min.Y } for _, c := range polygon { for j, p := range c { c[j].X = (p.X - min.X) * mul c[j].Y = (p.Y - min.Y) * mul } } r := safebbox(polygon) img2 := image.NewNRGBA(img.Bounds().Add(translate).Union(image.Rect(int(r.Min.X), int(r.Min.Y), int(r.Max.X), int(r.Max.Y)))) draw.Draw(img2, img2.Bounds(), image.Black, image.Pt(0, 0), draw.Src) draw.Draw(img2, img.Bounds().Add(translate), img, image.Pt(0, 0), draw.Src) img = img2 for _, c := range polygon { polyutil.DrawPolyline(c, brush(img, i)) } } return img }
// Text renders a text onto an image. // // TODO: support multiple lines func (f *Font) Text(dst draw.Image, text string, opts ...Option) error { o := options{ fg: color.White, } for _, opt := range opts { if err := opt(&o); err != nil { return err } } // X size 0 means preserve aspect ratio. // // Freetype docs say "You should not rely on the resulting glyphs // matching, or being constrained, to this pixel size." -- well, // we don't. We clip them to that bounding box, and don't crash or // corrupt memory. If it looks ugly, it's the font designer's // fault; switch to a better font. if err := f.font.SetSizePixels(0, dst.Bounds().Dy()); err != nil { return err } // Origin point for the baseline: left edge, lifted from the // bottom as much as the font descender says. // // TODO this results in a slightly ugly layout, with a lot of // negative space below the text, and less above; this is the // opposite of what one is supposed to do. The only fix, given // existing fonts with deep drops for "g" etc, is to just add // negative space above the text. Unfortunately, there seems to be // no good way to decide how much space to add; the state of the // art seems to be "expert human manually adjusts", which sucks. // // If we were to perform a similar calculation for the baseline, // starting from the drop and coming down by how much Ascender // tells us to, we'd end up in the same spot; that is of no use. pos := image.Point{ X: dst.Bounds().Min.X, // Descender is in dimensionless units, of which there are // UnitsPerEm in the width & height of the em box, which is // the size we asked to fit in our image. // // Because of us truncating subpixel alignment to nearest // pixel, this might might end up being off by 1, but if we // add floor/ceil here, we'll just shuffle that lost pixel // between top and bottom of the image. Perhaps we should give // SetSizePixels a pre-shrunk height? Y: dst.Bounds().Max.Y + f.font.Descender*dst.Bounds().Dy()/f.font.UnitsPerEm, } if debugBaseline { draw.Draw(dst, image.Rectangle{ Min: image.Point{X: dst.Bounds().Min.X, Y: pos.Y}, Max: image.Point{X: dst.Bounds().Max.X, Y: pos.Y + 1}, }, image.NewUniform(color.RGBA{G: 0xFF, A: 0xFF}), image.ZP, draw.Over) } var prev rune for _, ch := range text { glyph, err := f.font.Load(f.font.Index(ch)) if err != nil { return err } // All glyph measurements are in 26.6 format, and should be // pixel-aligned by now by the hinting process. Shift by 6 to // get pixels. i, err := glyph.Image() if err != nil { return err } if prev != 0 { x, _, err := f.font.Kerning(prev, ch) if err != nil { return err } pos.X += x >> 6 } prev = ch to := image.Rectangle{pos, dst.Bounds().Max} to.Min.X += glyph.HMetrics.BearingX >> 6 to.Min.Y -= glyph.HMetrics.BearingY >> 6 if debugGlyphHeight { y := pos.Y - glyph.HMetrics.BearingY>>6 draw.Draw(dst, image.Rectangle{ Min: image.Point{X: to.Min.X, Y: y}, Max: image.Point{X: to.Min.X + glyph.HMetrics.Advance>>6, Y: y + 1}, }, image.NewUniform(color.RGBA{R: 0xFF, B: 0xFF, A: 0xFF}), image.ZP, draw.Over) } draw.DrawMask(dst, to, image.NewUniform(o.fg), image.ZP, i, i.Bounds().Min, draw.Over) pos.X += glyph.HMetrics.Advance >> 6 } return nil }