Browse Source

Initial Commit

master 1.0.0
Gregory Rudolph 2 years ago
commit
ac61662d6d
Signed by: rudi
GPG Key ID: EF64F3CBD1A1EBDD
  1. 33
      .drone.yml
  2. 4
      .gitignore
  3. 20
      go.mod
  4. 40
      go.sum
  5. 187
      main.go
  6. 92
      sketch.go
  7. 33
      types.go

33
.drone.yml

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
kind: pipeline
type: docker
name: default
steps:
- name: Build
image: golang
commands:
- go get ./..
- go vet -v
- env
- GOOS="linux" go build -ldflags="-w -s" -o BackgroundDaemon-Linux
- GOOS="windows" go build -ldflags="-w -s" -o BackgroundDaemon-Win.exe
- GOOS="darwin" go build -ldflags="-w -s" -o BackgroundDaemon-macOS
- name: gitea_release
image: plugins/gitea-release
settings:
base_url: https://git.nightmare.haus
api_key:
from_secret: gitea_token
files:
- ./BackgroundDaemon-Linux
- ./BackgroundDaemon-Win.exe
- ./BackgroundDaemon-macOS
checksum:
- md5
- sha1
- sha256
- sha512
- adler32
- crc32
when:
event: [tag]

4
.gitignore vendored

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
*.png
*.jpg
*.jpeg
BackgroundDaemon

20
go.mod

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
module git.nightmare.haus/rudi/BackgroundDaemon
go 1.17
require (
github.com/fogleman/gg v1.3.0
github.com/reujab/wallpaper v0.0.0-20210630195606-5f9f655b3740
)
require (
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 // indirect
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

40
go.sum

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 h1:dy+DS31tGEGCsZzB45HmJJNHjur8GDgtRNX9U7HnSX4=
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 h1:qq2nCpSrXrmvDGRxW0ruW9BVEV1CN2a9YDOExdt+U0o=
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
github.com/reujab/wallpaper v0.0.0-20210630195606-5f9f655b3740 h1:X6IDPPN+zrSClp0Q+JiERA//d8L0WcU5MqcGeulCW1A=
github.com/reujab/wallpaper v0.0.0-20210630195606-5f9f655b3740/go.mod h1:WYwPVmM/8szeItLeWkwZSLRvQgrvsvstRzgznR8+E4Q=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 h1:5jaG59Zhd+8ZXe8C+lgiAGqkOaZBruqrWclLkgAww34=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

187
main.go

@ -0,0 +1,187 @@ @@ -0,0 +1,187 @@
package main
// imports
import (
"encoding/json"
"flag"
"fmt"
"image"
_ "image/jpeg"
"image/png"
"io"
"log"
"math/rand"
"net/http"
"os"
"time"
"github.com/kbinani/screenshot"
"github.com/reujab/wallpaper"
)
var (
sourceImgName string
outputImgName string
useNasa bool
useRandom bool
setWallpaper bool
useSketch bool
nasaAPI string
totalCycleCount = 5000
)
func init() {
flag.BoolVar(&useNasa, "nasa", false, "Use NASA Astronomy Pic Of the Day as a source image")
flag.BoolVar(&setWallpaper, "set", false, "Set user wallpaper to the output.")
flag.BoolVar(&useRandom, "unsplash", false, "Use random unsplash image for seed (Will grab dimensions from background or source if set)")
flag.BoolVar(&useSketch, "sketch", false, "Use Sketch to generate a new image.")
flag.StringVar(&sourceImgName, "source", "", "Source image, will override current background")
flag.StringVar(&outputImgName, "output", "out.png", "Output image filename")
flag.StringVar(&nasaAPI, "nasa-key", "DEMO_KEY", "Supply your own API Key for NASA APOD")
}
func main() {
flag.Parse()
background, err := wallpaper.Get()
if err != nil {
log.Panicln(err)
}
bounds := screenshot.GetDisplayBounds(0)
var img image.Image
if useRandom {
img, err = loadRandomUnsplashImage(bounds.Dx(), bounds.Dy())
if err != nil {
log.Panicln(err)
}
} else if useNasa {
resp, err := http.Get(fmt.Sprintf("https://api.nasa.gov/planetary/apod?api_key=%+v", nasaAPI))
if err != nil {
log.Fatalf("\nUnable to get NASA API Response\n")
return
}
var apod APODResponse
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&apod)
if err != nil {
log.Fatalf("\nUnable to decode APOD Response: \n%+v\n", resp)
return
}
file, err := os.CreateTemp(".", apod.Hdurl)
if err != nil {
log.Panicln(err)
}
defer os.Remove(file.Name())
downloadFile(file.Name(), apod.Hdurl)
img, err = loadImage(file.Name())
if err != nil {
log.Panicln(err)
}
} else if len(sourceImgName) > 0 {
img, err = loadImage(sourceImgName)
if err != nil {
log.Panicln(err)
}
} else {
img, err = loadImage(background)
if err != nil {
log.Println(background)
log.Panicln(err)
}
}
s := NewSketch(img, UserParams{
StrokeRatio: 0.75,
DestWidth: bounds.Dx(),
DestHeight: bounds.Dy(),
InitialAlpha: 0.1,
StrokeReduction: 0.002,
AlphaIncrease: 0.06,
StrokeInversionThreshold: 0.05,
StrokeJitter: int(0.1 * float64(bounds.Dx())),
MinEdgeCount: 3,
MaxEdgeCount: 8,
})
rand.Seed(time.Now().Unix())
if useSketch {
for i := 0; i < totalCycleCount; i++ {
s.Update()
}
saveOutput(s.Output(), outputImgName)
} else {
saveOutput(img, outputImgName)
}
if setWallpaper {
err = wallpaper.SetFromFile(outputImgName)
if err != nil {
log.Panicln(err)
}
}
}
func loadRandomUnsplashImage(width, height int) (image.Image, error) {
url := fmt.Sprintf("https://source.unsplash.com/random/%dx%d", width, height)
res, err := http.Get(url)
if err != nil {
return nil, err
}
defer res.Body.Close()
img, _, err := image.Decode(res.Body)
return img, err
}
func loadImage(src string) (image.Image, error) {
file, _ := os.Open(sourceImgName)
defer file.Close()
img, _, err := image.Decode(file)
return img, err
}
func saveOutput(img image.Image, filePath string) error {
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()
// Encode to `PNG` with `DefaultCompression` level
// then save to file
err = png.Encode(f, img)
if err != nil {
return err
}
return nil
}
// https://stackoverflow.com/questions/33845770/how-do-i-download-a-file-with-a-http-request-in-go-language/33845771
func downloadFile(filepath string, url string) (err error) {
// Create the file
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Writer the body to file
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}

92
sketch.go

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
package main
// imports
import (
"github.com/fogleman/gg"
"image"
"image/color"
"math/rand"
)
// imports
type UserParams struct {
StrokeRatio float64
DestWidth int
DestHeight int
InitialAlpha float64
StrokeReduction float64
AlphaIncrease float64
StrokeInversionThreshold float64
StrokeJitter int
MinEdgeCount int
MaxEdgeCount int
}
type Sketch struct {
UserParams // embed for easier access
source image.Image
dc *gg.Context
sourceWidth int
sourceHeight int
strokeSize float64
initialStrokeSize float64
}
func NewSketch(source image.Image, userParams UserParams) *Sketch {
s := &Sketch{UserParams: userParams}
bounds := source.Bounds()
s.sourceWidth, s.sourceHeight = bounds.Max.X, bounds.Max.Y
s.initialStrokeSize = s.StrokeRatio * float64(s.DestWidth)
s.strokeSize = s.initialStrokeSize
canvas := gg.NewContext(s.DestWidth, s.DestHeight)
canvas.SetColor(color.Black)
canvas.DrawRectangle(0, 0, float64(s.DestWidth), float64(s.DestHeight))
canvas.FillPreserve()
s.source = source
s.dc = canvas
return s
}
func (s *Sketch) Update() {
rndX := rand.Float64() * float64(s.sourceWidth)
rndY := rand.Float64() * float64(s.sourceHeight)
r, g, b := rgb255(s.source.At(int(rndX), int(rndY)))
destX := rndX * float64(s.DestWidth) / float64(s.sourceWidth)
destX += float64(randRange(s.StrokeJitter))
destY := rndY * float64(s.DestHeight) / float64(s.sourceHeight)
destY += float64(randRange(s.StrokeJitter))
edges := s.MinEdgeCount + rand.Intn(s.MaxEdgeCount-s.MinEdgeCount+1)
s.dc.SetRGBA255(r, g, b, int(s.InitialAlpha))
s.dc.DrawRegularPolygon(edges, destX, destY, s.strokeSize, rand.Float64())
s.dc.FillPreserve()
if s.strokeSize <= s.StrokeInversionThreshold*s.initialStrokeSize {
if (r+g+b)/3 < 128 {
s.dc.SetRGBA255(255, 255, 255, int(s.InitialAlpha*2))
} else {
s.dc.SetRGBA255(0, 0, 0, int(s.InitialAlpha*2))
}
}
s.dc.Stroke()
s.strokeSize -= s.StrokeReduction * s.strokeSize
s.InitialAlpha += s.AlphaIncrease
}
func (s *Sketch) Output() image.Image {
return s.dc.Image()
}
func rgb255(c color.Color) (r, g, b int) {
r0, g0, b0, _ := c.RGBA()
return int(r0 / 257), int(g0 / 257), int(b0 / 257)
}
func randRange(max int) int {
return -max + rand.Intn(2*max)
}

33
types.go

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
package main
type APODResponse struct {
Date string `json:"date"`
Explanation string `json:"explanation"`
Hdurl string `json:"hdurl"`
MediaType string `json:"media_type"`
ServiceVersion string `json:"service_version"`
Title string `json:"title"`
URL string `json:"url"`
}
type MarsResponse struct {
Photos []struct {
ID int `json:"id"`
Sol int `json:"sol"`
Camera struct {
ID int `json:"id"`
Name string `json:"name"`
RoverID int `json:"rover_id"`
FullName string `json:"full_name"`
} `json:"camera"`
ImgSrc string `json:"img_src"`
EarthDate string `json:"earth_date"`
Rover struct {
ID int `json:"id"`
Name string `json:"name"`
LandingDate string `json:"landing_date"`
LaunchDate string `json:"launch_date"`
Status string `json:"status"`
} `json:"rover"`
} `json:"photos"`
}
Loading…
Cancel
Save