// Halves the width of the double-width image created by hxl to produce nice // smooth edges. func halveWidth(img image.Image) image.Image { b := img.Bounds() o := image.NewRGBA(image.Rect(0, 0, b.Dx()/2, b.Dy())) for y := 0; y < b.Dy(); y++ { for x := 0; x < b.Dx()/2; x++ { l := img.At(x*2, y) r := img.At(x*2+1, y) o.Set(x, y, utils.Average(l, r)) } } return o }
// Pixelate takes an Image and pixelates it into rectangles with the dimensions // given. The colour values in each region are averaged to produce the resulting // colours. func Pixelate(img image.Image, size utils.Dimension) image.Image { b := img.Bounds() cols := b.Dx() / size.W rows := b.Dy() / size.H o := image.NewRGBA(image.Rect(0, 0, size.W*cols, size.H*rows)) for col := 0; col < cols; col++ { for row := 0; row < rows; row++ { values := make([]color.Color, size.H*size.W) count := 0 for y := 0; y < size.H; y++ { for x := 0; x < size.W; x++ { realY := row*size.H + y realX := col*size.W + x values[count] = img.At(realX, realY) count++ } } avg := utils.Average(values...) for y := 0; y < size.H; y++ { for x := 0; x < size.W; x++ { realY := row*size.H + y realX := col*size.W + x o.Set(realX, realY, avg) } } } } return o }
func halve(img image.Image, size utils.Dimension) image.Image { b := img.Bounds() o := image.NewRGBA(image.Rect(0, 0, b.Dx()/2, b.Dy()/2)) for y := 0; y < b.Dy()/2; y++ { for x := 0; x < b.Dx()/2; x++ { tl := img.At(x*2, y*2) tr := img.At(x*2+1, y*2) br := img.At(x*2+1, y*2-1) bl := img.At(x*2, y*2-1) if y%size.H == 0 { o.Set(x, y, tl) } else if x%size.W == 0 { o.Set(x, y, tl) } else { o.Set(x, y, utils.Average(tl, tr, bl, br)) } } } return o }
// Vxl pixelates the Image into isometric cubes. It averages the colours and // naïvely darkens and lightens the colours to mimic highlight and shade. func Vxl(img image.Image, height int, flip bool, top, left, right float64) image.Image { b := img.Bounds() pixelHeight := height pixelWidth := int(math.Sqrt(3.0) * float64(pixelHeight) / 2.0) cols := b.Dx() / pixelWidth rows := b.Dy() / pixelHeight // intersection of lines c := float64(pixelHeight) / 2 // gradient of lines k := math.Sqrt(3.0) / 3.0 o := image.NewRGBA(image.Rect(0, 0, pixelWidth*cols, pixelHeight*rows)) // See: http://www.flickr.com/photos/hawx-/8466236036/ inTopSquare := func(x, y float64) bool { if !flip { y *= -1 } return y <= -k*x+c && y >= k*x && y >= -k*x && y <= k*x+c } inBottomRight := func(x, y float64) bool { if !flip { y *= -1 } return x >= 0 && y <= k*x && y >= k*x-c } inBottomLeft := func(x, y float64) bool { if !flip { y *= -1 } return x <= 0 && y <= -k*x && y >= -k*x-c } inHexagon := func(x, y float64) bool { return inTopSquare(x, y) || inBottomRight(x, y) || inBottomLeft(x, y) } topL := channel.LightnessC(utils.Multiplier(top)) rightL := channel.LightnessC(utils.Multiplier(right)) leftL := channel.LightnessC(utils.Multiplier(left)) for col := 0; col < cols; col++ { for row := 0; row < rows; row++ { seen := []color.Color{} for y := 0; y < pixelHeight; y++ { for x := 0; x < pixelWidth; x++ { realY := row*(pixelHeight+int(c)) + y realX := col*pixelWidth + x pixel := img.At(realX, realY) y_origin := float64(y - pixelHeight/2) x_origin := float64(x - pixelWidth/2) if inHexagon(x_origin, y_origin) { seen = append(seen, pixel) } } } average := utils.Average(seen...) for y := 0; y < pixelHeight; y++ { for x := 0; x < pixelWidth; x++ { realY := row*(pixelHeight+int(c)) + y realX := col*pixelWidth + x y_origin := float64(y - pixelHeight/2) x_origin := float64(x - pixelWidth/2) // This stops white bits showing above the top squares. It does mean // the dimensions aren't perfect, but what did you expect with pixels // and trig. It is inefficient though, maybe fix that later? if (!flip && y_origin < 0) || (flip && y_origin > 0) { o.Set(realX, realY, topL(average)) } else { if x_origin > 0 { o.Set(realX, realY, rightL(average)) } else { o.Set(realX, realY, leftL(average)) } } if inTopSquare(x_origin, y_origin) { o.Set(realX, realY, topL(average)) } if inBottomRight(x_origin, y_origin) { o.Set(realX, realY, rightL(average)) } if inBottomLeft(x_origin, y_origin) { o.Set(realX, realY, leftL(average)) } } } } } offsetY := (pixelHeight + int(c)) / 2.0 offsetX := pixelWidth / 2 for col := -1; col < cols; col++ { for row := -1; row < rows; row++ { seen := []color.Color{} for y := 0; y < pixelHeight; y++ { for x := 0; x < pixelWidth; x++ { realY := row*(pixelHeight+int(c)) + y + offsetY realX := col*pixelWidth + x + offsetX if image.Pt(realX, realY).In(b) { pixel := img.At(realX, realY) y_origin := float64(y - pixelHeight/2) x_origin := float64(x - pixelWidth/2) if inHexagon(x_origin, y_origin) { seen = append(seen, pixel) } } } } if len(seen) <= 0 { continue } average := utils.Average(seen...) for y := 0; y < pixelHeight; y++ { for x := 0; x < pixelWidth; x++ { realY := row*(pixelHeight+int(c)) + y + offsetY realX := col*pixelWidth + x + offsetX y_origin := float64(y - pixelHeight/2) x_origin := float64(x - pixelWidth/2) if inTopSquare(x_origin, y_origin) { o.Set(realX, realY, topL(average)) } if inBottomRight(x_origin, y_origin) { o.Set(realX, realY, rightL(average)) } if inBottomLeft(x_origin, y_origin) { o.Set(realX, realY, leftL(average)) } } } } } return o }
func pxlDo(img image.Image, triangle int, size utils.Dimension, scaleFactor int) image.Image { b := img.Bounds() cols := b.Dx() / size.W rows := b.Dy() / size.H ratio := float64(size.H) / float64(size.W) o := image.NewRGBA(image.Rect(0, 0, size.W*cols*scaleFactor, size.H*rows*scaleFactor)) inTop := func(x, y float64) bool { return (y > ratio*x) && (y > ratio*-x) } inRight := func(x, y float64) bool { return (y < ratio*x) && (y > ratio*-x) } inBottom := func(x, y float64) bool { return (y < ratio*x) && (y < ratio*-x) } inLeft := func(x, y float64) bool { return (y > ratio*x) && (y < ratio*-x) } for col := 0; col < cols; col++ { for row := 0; row < rows; row++ { to := []color.Color{} ri := []color.Color{} bo := []color.Color{} le := []color.Color{} for y := 0; y < size.H; y++ { for x := 0; x < size.W; x++ { realY := row*size.H + y realX := col*size.W + x y_origin := float64(y - size.H/2) x_origin := float64(x - size.W/2) if inTop(x_origin, y_origin) { to = append(to, img.At(realX, realY)) } else if inRight(x_origin, y_origin) { ri = append(ri, img.At(realX, realY)) } else if inBottom(x_origin, y_origin) { bo = append(bo, img.At(realX, realY)) } else if inLeft(x_origin, y_origin) { le = append(le, img.At(realX, realY)) } } } ato := utils.Average(to...) ari := utils.Average(ri...) abo := utils.Average(bo...) ale := utils.Average(le...) if (triangle != LEFT) && (triangle == RIGHT || utils.Closeness(ato, ari) > utils.Closeness(ato, ale)) { top_right := utils.Average(ato, ari) bottom_left := utils.Average(abo, ale) for y := 0; y < size.H*scaleFactor; y++ { for x := 0; x < size.W*scaleFactor; x++ { realY := row*size.H*scaleFactor + y realX := col*size.W*scaleFactor + x y_origin := float64(y - size.H*scaleFactor/2) x_origin := float64(x - size.W*scaleFactor/2) if y_origin > ratio*x_origin { o.Set(realX, realY, top_right) } else { o.Set(realX, realY, bottom_left) } } } } else { top_left := utils.Average(ato, ale) bottom_right := utils.Average(abo, ari) for y := 0; y < size.H*scaleFactor; y++ { for x := 0; x < size.W*scaleFactor; x++ { realY := row*size.H*scaleFactor + y realX := col*size.W*scaleFactor + x y_origin := float64(y - size.H*scaleFactor/2) x_origin := float64(x - size.W*scaleFactor/2) if y_origin >= ratio*-x_origin { o.Set(realX, realY, top_left) } else { o.Set(realX, realY, bottom_right) } } } } } } return o }
// Hxl pixelates the Image into equilateral triangles with the width // given. These are arranged into hexagonal shapes. func Hxl(img image.Image, width int) image.Image { b := img.Bounds() pixelHeight := width * 2 pixelWidth := width cols := b.Dx() / pixelWidth rows := b.Dy() / pixelHeight o := image.NewRGBA(image.Rect(0, 0, pixelWidth*cols*2, pixelHeight*rows)) // Note: "Top" doesn't mean above the x-axis, it means in the triangle // pointing towards the x-axis. inTop := func(x, y float64) bool { return (x >= 0 && y >= x) || (x <= 0 && y >= -x) } // Same for "Bottom" this is the triangle below and pointing towards the // x-axis. inBottom := func(x, y float64) bool { return (x >= 0 && y <= -x) || (x <= 0 && y <= x) } for col := 0; col < cols; col++ { for row := 0; row < rows; row++ { north := []color.Color{} south := []color.Color{} for y := 0; y < pixelHeight; y++ { for x := 0; x < pixelWidth; x++ { realY := row*pixelHeight + y realX := col*pixelWidth + x pixel := img.At(realX, realY) y_origin := float64(y - pixelHeight/2) x_origin := float64(x - pixelWidth/2) if inTop(x_origin, y_origin) { north = append(north, pixel) } else if inBottom(x_origin, y_origin) { south = append(south, pixel) } } } top := utils.Average(north...) bot := utils.Average(south...) for y := 0; y < pixelHeight; y++ { for x := 0; x < pixelWidth*2; x++ { realY := row*pixelHeight + y realX := col*pixelWidth*2 + x y_origin := float64(y - pixelHeight/2) x_origin := float64(x - pixelWidth*2/2) if inTop(x_origin, y_origin) { o.Set(realX, realY, top) } else if inBottom(x_origin, y_origin) { o.Set(realX, realY, bot) } } } } } // Now for the shifted version offsetY := pixelHeight / 2 offsetX := pixelWidth / 2 for col := -1; col < cols; col++ { for row := -1; row < rows; row++ { north := []color.Color{} south := []color.Color{} for y := 0; y < pixelHeight; y++ { for x := 0; x < pixelWidth; x++ { realY := row*pixelHeight + y + offsetY realX := col*pixelWidth + x + offsetX if realX >= 0 && realX < b.Dx() { pixel := img.At(realX, realY) y_origin := float64(y - pixelHeight/2) x_origin := float64(x - pixelWidth/2) if inTop(x_origin, y_origin) { north = append(north, pixel) } else if inBottom(x_origin, y_origin) { south = append(south, pixel) } } } } top := utils.Average(north...) bot := utils.Average(south...) for y := 0; y < pixelHeight; y++ { for x := 0; x < pixelWidth*2; x++ { realY := row*pixelHeight + y + offsetY realX := col*pixelWidth*2 + x + offsetX*2 y_origin := float64(y - pixelHeight/2) x_origin := float64(x - pixelWidth*2/2) if inTop(x_origin, y_origin) { o.Set(realX, realY, top) } else if inBottom(x_origin, y_origin) { o.Set(realX, realY, bot) } } } } } return halveWidth(o) }