Skip to content

Instantly share code, notes, and snippets.

@tav
Created February 5, 2014 16:55
Show Gist options
  • Save tav/8828214 to your computer and use it in GitHub Desktop.
Save tav/8828214 to your computer and use it in GitHub Desktop.
// Public Domain (-) 2014 The Wikifactory Site Authors.
// See the Wikifactory Site UNLICENSE file for details.
package main
import (
"code.google.com/p/freetype-go/freetype/raster"
"code.google.com/p/freetype-go/freetype/truetype"
"errors"
"image"
"image/draw"
"io/ioutil"
"math/rand"
"strings"
"sync"
"time"
)
const (
dpi = 300.0
text = "Wikifactory"
width = 1000
height = 128
maxSize = 24
)
var InvalidSize = errors.New("logo: invalid size parameter")
var fontFiles = []string{
"fonts/brandon-printed-one.ttf",
"fonts/brandon-printed-two.ttf",
"fonts/brandon-printed-double.ttf",
// "fonts/brandon-printed-inline.ttf",
// "fonts/brandon-printed-one-shadow.ttf",
}
var (
fonts = []*truetype.Font{}
glyphset = [maxSize + 1][]*glyph{}
mutex = sync.RWMutex{}
variants = len(fontFiles) * 2
)
var r = raster.NewRasterizer(0, 0)
func renderLogo(size int) (image.Image, error) {
if size <= 0 || size > maxSize {
return nil, InvalidSize
}
glyphs := getGlyphs(size)
ctx := image.Rect(0, 0, width, height)
img := image.NewRGBA(ctx)
for i := 0; i < len(text); i++ {
choice := (rand.Intn(variants) * len(text)) + i
glyph := glyphs[choice]
draw.DrawMask(img, glyph.dr, image.Black, image.ZP, glyph.mask, glyph.mp, draw.Over)
}
return img, nil
}
func setup() error {
rand.Seed(time.Now().UnixNano())
for _, path := range fontFiles {
file, err := ioutil.ReadFile(path)
if err != nil {
return err
}
font, err := truetype.Parse(file)
if err != nil {
return err
}
fonts = append(fonts, font)
}
getGlyphs(24)
return nil
}
type glyph struct {
mask *image.Alpha
mp image.Point
dr image.Rectangle
}
func getGlyphs(size int) []*glyph {
mutex.RLock()
glyphs := glyphset[size]
mutex.RUnlock()
if glyphs != nil {
return glyphs
}
mutex.Lock()
defer mutex.Unlock()
glyphs = []*glyph{}
for _, font := range fonts {
glyphs = append(glyphs, genGlyphs(font, size)...)
}
glyphset[size] = glyphs
return glyphs
}
func Pt(x, y int) raster.Point {
return raster.Point{
X: raster.Fix32(x << 8),
Y: raster.Fix32(y << 8),
}
}
func pointToFix32(x float64) raster.Fix32 {
return raster.Fix32(x * dpi * (256.0 / 72.0))
}
func drawContour(r *raster.Rasterizer, ps []truetype.Point, dx, dy raster.Fix32) {
if len(ps) == 0 {
return
}
// ps[0] is a truetype.Point measured in FUnits and positive Y going upwards.
// start is the same thing measured in fixed point units and positive Y
// going downwards, and offset by (dx, dy)
start := raster.Point{
X: dx + raster.Fix32(ps[0].X<<2),
Y: dy - raster.Fix32(ps[0].Y<<2),
}
r.Start(start)
q0, on0 := start, true
for _, p := range ps[1:] {
q := raster.Point{
X: dx + raster.Fix32(p.X<<2),
Y: dy - raster.Fix32(p.Y<<2),
}
on := p.Flags&0x01 != 0
if on {
if on0 {
r.Add1(q)
} else {
r.Add2(q0, q)
}
} else {
if on0 {
// No-op.
} else {
mid := raster.Point{
X: (q0.X + q.X) / 2,
Y: (q0.Y + q.Y) / 2,
}
r.Add2(q0, mid)
}
}
q0, on0 = q, on
}
// Close the curve.
if on0 {
r.Add1(start)
} else {
r.Add2(q0, start)
}
}
func genGlyphs(font *truetype.Font, size int) (glyphs []*glyph) {
scale := int32(float64(size) * dpi * (64.0 / 72.0))
clip := image.Rect(0, 0, width, height)
// Calculate the rasterizer's bounds to handle the largest glyph.
b := font.Bounds(scale)
xmin := +int(b.XMin) >> 6
ymin := -int(b.YMax) >> 6
xmax := +int(b.XMax+63) >> 6
ymax := -int(b.YMin-63) >> 6
r := raster.NewRasterizer(xmax-xmin, ymax-ymin)
buf := truetype.NewGlyphBuf()
for _, variant := range []string{strings.ToUpper(text), strings.ToLower(text)} {
pt := Pt(30, 10+int(pointToFix32(float64(size))>>8))
for _, char := range variant {
idx := font.Index(char)
buf.Load(font, scale, idx, truetype.FullHinting)
// Calculate the integer-pixel bounds for the glyph.
xmin := int(raster.Fix32(buf.B.XMin<<2)) >> 8
ymin := int(-raster.Fix32(buf.B.YMax<<2)) >> 8
xmax := int(raster.Fix32(buf.B.XMax<<2)+0xff) >> 8
ymax := int(-raster.Fix32(buf.B.YMin<<2)+0xff) >> 8
fx := raster.Fix32(-xmin << 8)
fy := raster.Fix32(-ymin << 8)
ix := int(pt.X >> 8)
iy := int(pt.Y >> 8)
// Rasterize the glyph's vectors.
r.Clear()
e0 := 0
for _, e1 := range buf.End {
drawContour(r, buf.Point[e0:e1], fx, fy)
e0 = e1
}
mask := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
r.Rasterize(raster.NewAlphaSrcPainter(mask))
pt.X += raster.Fix32(buf.AdvanceWidth << 2)
offset := image.Point{xmin, ymin}
offset = offset.Add(image.Point{ix, iy})
glyphRect := mask.Bounds().Add(offset)
dr := clip.Intersect(glyphRect)
mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y}
glyphs = append(glyphs, &glyph{
mask: mask,
mp: mp,
dr: dr,
})
}
}
return
}
// Public Domain (-) 2014 The Wikifactory Site Authors.
// See the Wikifactory Site UNLICENSE file for details.
package main
import (
"fmt"
"image/png"
"net/http"
)
func handleLogo(w http.ResponseWriter, req *http.Request) {
logo, err := renderLogo(24)
if err != nil {
fmt.Fprintf(w, "ERROR: %s\n", err)
return
}
err = png.Encode(w, logo)
if err != nil {
fmt.Fprintf(w, "ERROR: %s\n", err)
return
}
w.Header().Set("Content-Type", "image/png")
fmt.Printf("SERVED: /logo\n")
}
func main() {
err := setup()
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
}
http.HandleFunc("/logo", handleLogo)
err = http.ListenAndServe(":9093", nil)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment