Skip to content

Instantly share code, notes, and snippets.

@Unkn0wnCat
Last active April 18, 2022 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Unkn0wnCat/b0373537d66b9b0fe3293e6634591614 to your computer and use it in GitHub Desktop.
Save Unkn0wnCat/b0373537d66b9b0fe3293e6634591614 to your computer and use it in GitHub Desktop.
Accidental "snow camo" generator written in GoLang
/**
Copyright 2022 Kevin Kandlbinder (https://kevink.dev)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"github.com/google/hilbert"
"image"
"image/color"
"image/png"
"log"
"math"
"math/rand"
"os"
)
const (
sizeMultiplier = 15 // This will take a long time and a lot of CPU and RAM power, change to 12 if you are reasonable
threshold = 6 // The lower this is the less often colors will switch (higher threshold = more randomness)
)
func main() {
pixels := math.Pow(2, sizeMultiplier) // Calculate length of image sides
numMax := math.Pow(pixels, 2) // Calculate complete number of pixels
// Make image with size of {pixels}x{pixels}
img := image.NewRGBA(image.Rectangle{
Min: image.Point{},
Max: image.Point{X: int(pixels), Y: int(pixels)},
})
// Initialize hilbert curve helper
s, err := hilbert.NewHilbert(int(pixels))
if err != nil {
log.Panicln(err)
}
// Initialize look-back array
var lookBack []bool
// Iterate over all pixels in the image
for n := 0; n < int(numMax); n++ {
lookBack = fill(n, lookBack, s, img)
}
// Create the file to save the image into
f, _ := os.Create("image.png")
defer f.Close()
// Write PNG image to file
png.Encode(f, img)
}
func fill(n int, lookBack []bool, s *hilbert.Hilbert, img *image.RGBA) []bool {
// Random number 0-100
randNum := rand.Intn(100)
// If our number is over the threshold, we will use the color most predominant over the last 10 pixels
conform := randNum > threshold
// Count number of true or false values over the last 10 pixels
numF := 0
numT := 0
for _, rec := range lookBack {
if rec {
numT++
continue
}
numF++
}
// Set myVal to the most predominant value over the last 10 pixels
myVal := numT > numF
if !conform {
myVal = !myVal // If we don't want to conform, invert value
}
// Map to x,y coords using hilbert curve helper
x, y, err := s.Map(n)
if err != nil {
log.Panicln(err)
return nil
}
if len(lookBack) > 10 {
lookBack = lookBack[1:]
}
lookBack = append(lookBack, myVal)
// If myVal is set, change pixel to black and return
if myVal {
img.Set(x, y, color.RGBA{
R: 0,
G: 0,
B: 0,
A: 0xff,
})
return lookBack
}
// Otherwise, set pixel to white and return
img.Set(x, y, color.RGBA{
R: 255,
G: 255,
B: 255,
A: 0xff,
})
return lookBack
}
/**
Copyright 2022 Kevin Kandlbinder (https://kevink.dev)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"github.com/google/hilbert"
"image"
"image/color"
"image/draw"
"image/gif"
"image/png"
"log"
"math"
"math/rand"
"os"
)
const (
sizeMultiplier = 12 // This will take a long time and a lot of CPU and RAM power, change to 12 if you are reasonable, 15 if you're cool
threshold = 0.0001 // The lower this is the less often colors will switch (higher threshold = more randomness) (0.0001 / 0.00005)
createGif = false
)
var (
fleckTarn = ColorPack{
ColorWeights: []float32{.1, .25, .35, .2, .1},
Colors: []color.RGBA{
{0xa7, 0xad, 0x6d, 0xff}, // Hellgrün
{0x61, 0x68, 0x47, 0xff}, // Helloliv
{0x43, 0x4a, 0x39, 0xff}, // Dunkelgrün
{0x76, 0x4a, 0x2f, 0xff}, // Braun
{0x31, 0x31, 0x31, 0xff}, // Schwarz
},
}
russTarn = ColorPack{
ColorWeights: []float32{.2, .2, .2, .2, .2},
Colors: []color.RGBA{
{0x2c, 0x2e, 0x25, 0xff}, // Pine Tree
{0x50, 0x54, 0x3d, 0xff}, // Olive Drab Camouflage
{0xa3, 0xa1, 0x84, 0xff}, // Grullo
{0x8b, 0x7f, 0x57, 0xff}, // Shadow
{0x63, 0x5d, 0x40, 0xff}, // Quincy
},
}
)
type ColorPack struct {
ColorWeights []float32
Colors []color.RGBA
}
func chooseColor(palette ColorPack) color.RGBA {
randChoice := rand.Float32()
for i, weight := range palette.ColorWeights {
randChoice -= weight
if randChoice <= 0 {
return palette.Colors[i]
}
}
panic("WHAT?!")
}
func main() {
//rand.Seed(time.Now().UnixMilli())
rand.Seed(420)
_ = fleckTarn
colorPack := russTarn
var palette = color.Palette{colorPack.Colors[0], colorPack.Colors[1], colorPack.Colors[2], colorPack.Colors[3], colorPack.Colors[4], color.RGBA{0, 0, 0, 0}}
pixels := math.Pow(2, sizeMultiplier) // Calculate length of image sides
numMax := math.Pow(pixels, 2) // Calculate complete number of pixels
// Make image with size of {pixels}x{pixels}
img := image.NewRGBA(image.Rectangle{
Min: image.Point{},
Max: image.Point{X: int(pixels), Y: int(pixels)},
})
// Initialize hilbert curve helper
s, err := hilbert.NewHilbert(int(pixels))
if err != nil {
log.Panicln(err)
}
var images []*image.Paletted
var delays []int
fmt.Println("Starting to generate picture...")
currentColor := chooseColor(colorPack)
// Iterate over all pixels in the image
for n := 0; n < int(numMax); n++ {
currentColor = fill(n, s, img, currentColor, colorPack)
if createGif && math.Mod(float64(n), 250000) == 0 {
bounds := img.Bounds()
palettedImage := image.NewPaletted(bounds, palette)
draw.Draw(palettedImage, palettedImage.Rect, img, bounds.Min, draw.Over)
images = append(images, palettedImage)
delays = append(delays, 0)
}
if math.Mod(float64(n), 1000000) == 0 {
fmt.Print(".")
if n != 0 && math.Mod(float64(n), 100*1000000) == 0 {
fmt.Println()
}
}
}
if createGif {
bounds := img.Bounds()
palettedImage := image.NewPaletted(bounds, palette)
draw.Draw(palettedImage, palettedImage.Rect, img, bounds.Min, draw.Over)
images = append(images, palettedImage)
delays = append(delays, 1)
}
fmt.Println()
fmt.Println("Saving picture...")
// Create the file to save the image into
f, _ := os.Create("image.png")
defer f.Close()
// Write PNG image to file
png.Encode(f, img)
if createGif {
f2, _ := os.OpenFile("anim.gif", os.O_WRONLY|os.O_CREATE, 0600)
defer f2.Close()
err = gif.EncodeAll(f2, &gif.GIF{
Image: images,
Delay: delays,
})
if err != nil {
log.Panicln(err)
}
}
fmt.Println("Done.")
}
func fill(n int, s *hilbert.Hilbert, img *image.RGBA, prevColor color.RGBA, colorPack ColorPack) color.RGBA {
// Random number 0-1
randNum := rand.Float32()
// If our number is over the threshold, we will continue using the current color
conform := randNum > threshold
// Set myVal to previous color
myVal := prevColor
if !conform {
myVal = chooseColor(colorPack)
}
// Map to x,y coords using hilbert curve helper
x, y, err := s.Map(n)
if err != nil {
log.Panicln(err)
return chooseColor(colorPack)
}
img.Set(x, y, myVal)
return myVal
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment