package entity import ( "image" "image/color" "image/draw" ) const ( collageSize = 480 collageGap = 8 ) var ( collageBG = color.RGBA{R: 237, G: 237, B: 237, A: 255} collageFill = color.RGBA{R: 216, G: 216, B: 216, A: 255} ) // PlaceholderFace is a solid tile used when a member avatar cannot be loaded. func PlaceholderFace() *image.RGBA { img := image.NewRGBA(image.Rect(0, 0, 256, 256)) draw.Draw(img, img.Bounds(), &image.Uniform{C: collageFill}, image.Point{}, draw.Src) return img } // Collage tiles as many member avatars as given (1–9). Two people are two tiles, not a 3×3 grid. func Collage(faces []image.Image) *image.RGBA { if len(faces) > MaxCollageFaces { faces = faces[:MaxCollageFaces] } dst := image.NewRGBA(image.Rect(0, 0, collageSize, collageSize)) draw.Draw(dst, dst.Bounds(), &image.Uniform{C: collageBG}, image.Point{}, draw.Src) cells := collageCells(len(faces)) for i := range cells { r := cells[i] src := image.Image(&image.Uniform{C: collageFill}) if i < len(faces) && faces[i] != nil { src = faces[i] } blitSquare(dst, src, r) } return dst } func collageCells(n int) []image.Rectangle { if n < 1 { return nil } if n > MaxCollageFaces { n = MaxCollageFaces } cols := 1 if n >= 5 { cols = 3 } else if n >= 2 { cols = 2 } rows := (n + cols - 1) / cols cell := (collageSize - collageGap*(cols+1)) / cols gridW := cols*cell + collageGap*(cols-1) gridH := rows*cell + collageGap*(rows-1) ox := (collageSize - gridW) / 2 oy := (collageSize - gridH) / 2 first := n % cols if first == 0 { first = cols } out := make([]image.Rectangle, 0, n) i := 0 for row := 0; row < rows && i < n; row++ { count := cols if row == 0 { count = first } rowW := count*cell + collageGap*(count-1) x0 := ox + (gridW-rowW)/2 y := oy + row*(cell+collageGap) for c := 0; c < count && i < n; c++ { x := x0 + c*(cell+collageGap) out = append(out, image.Rect(x, y, x+cell, y+cell)) i++ } } return out } func blitSquare(dst *image.RGBA, src image.Image, r image.Rectangle) { sb := src.Bounds() side := sb.Dx() if h := sb.Dy(); h < side { side = h } if side < 1 || r.Empty() { return } sx0 := sb.Min.X + (sb.Dx()-side)/2 sy0 := sb.Min.Y + (sb.Dy()-side)/2 w, h := r.Dx(), r.Dy() for y := 0; y < h; y++ { sy := sy0 + y*side/h for x := 0; x < w; x++ { dst.Set(r.Min.X+x, r.Min.Y+y, src.At(sx0+x*side/w, sy)) } } }