Skip to content

Commit f09f5a5

Browse files
committed
[IMPROVE] Cache renders and use WEBP instead of png. Lowers overall CPU usage and render performance
1 parent 073cd7b commit f09f5a5

6 files changed

Lines changed: 52 additions & 18 deletions

File tree

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212

1313
require (
1414
github.com/cespare/xxhash/v2 v2.3.0 // indirect
15+
github.com/chai2010/webp v1.1.1 // indirect
1516
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
1617
golang.org/x/net v0.38.0 // indirect
1718
golang.org/x/sys v0.31.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
22
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
3+
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
4+
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
35
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
46
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
57
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=

http.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ package main
22

33
import (
44
"fmt"
5+
"github.com/chai2010/webp"
56
"github.com/gorilla/mux"
6-
"image/png"
7+
"io"
78
"net/http"
9+
"os"
10+
"path"
811
"strconv"
912
"strings"
13+
"time"
1014
)
1115

1216
// Init the HTTP (or rest api) server
@@ -52,6 +56,14 @@ func handleAvatar(w http.ResponseWriter, r *http.Request) {
5256
}
5357
}
5458

59+
// To prevent overload on the server, we enforce some limits on scaling.
60+
// Not smaller than 16, so that it's visible, and not more than 1024px
61+
if scale < 16 {
62+
scale = 16
63+
} else if scale > 1024 {
64+
scale = 1024
65+
}
66+
5567
var uuid string
5668
var err error
5769

@@ -72,6 +84,18 @@ func handleAvatar(w http.ResponseWriter, r *http.Request) {
7284
}
7385
}
7486

87+
cachePath := path.Join(renderDir, fmt.Sprintf("%s_%s_%s.web", mode, uuid, strconv.Itoa(scale)))
88+
cachedFile, err := os.Open(cachePath)
89+
if err == nil {
90+
w.Header().Set("Content-Type", "image/webp")
91+
_, err = io.Copy(w, cachedFile)
92+
if err != nil {
93+
http.Error(w, "Failed to read image: "+err.Error(), http.StatusInternalServerError)
94+
}
95+
return
96+
}
97+
defer cachedFile.Close()
98+
7599
// Request the skin from the MOJANG servers
76100
skinPath, err := fetchSkin(uuid)
77101
if err != nil {
@@ -86,10 +110,18 @@ func handleAvatar(w http.ResponseWriter, r *http.Request) {
86110
}
87111

88112
// Encode the image ready for browser rendering
89-
w.Header().Set("Content-Type", "image/png")
90-
err = png.Encode(w, img)
113+
f, _ := os.Create(cachePath)
114+
err = webp.Encode(f, img, &webp.Options{
115+
Lossless: true,
116+
Quality: 100,
117+
Exact: true,
118+
})
119+
91120
if err != nil {
92-
http.Error(w, "Failed to encode image", http.StatusInternalServerError)
121+
http.Error(w, "Failed to encode image: "+err.Error(), http.StatusInternalServerError)
93122
return
94123
}
124+
125+
w.Header().Set("Content-Type", "image/webp")
126+
http.ServeContent(w, r, cachePath, time.Now(), f)
95127
}

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import (
66

77
// Storage directory for skin cache
88
const skinCacheDir = "cached_skins"
9+
const renderDir = "cached_skins/render"
910

1011
// Main entry point
1112
func main() {
1213
os.MkdirAll(skinCacheDir, os.ModePerm)
14+
os.MkdirAll(renderDir, os.ModePerm)
1315
cleanupCache()
1416
initRedis()
1517
initHttp()

skins.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import (
44
"encoding/base64"
55
"encoding/json"
66
"fmt"
7+
"github.com/chai2010/webp"
78
"github.com/mineatar-io/skin-render"
89
"image"
9-
"image/png"
1010
"io/ioutil"
1111
"net/http"
1212
"os"
@@ -15,7 +15,7 @@ import (
1515

1616
// Skin fetcher. Here, we try to fetch the skin from the cache, via mojangs servers, and finally, fall back to crafthead.
1717
func fetchSkin(uuid string) (string, error) {
18-
cachePath := filepath.Join(skinCacheDir, uuid+".png")
18+
cachePath := filepath.Join(skinCacheDir, uuid+".webp")
1919
if _, err := os.Stat(cachePath); err == nil {
2020
return cachePath, nil
2121
}
@@ -75,19 +75,11 @@ func renderSkin(skinPath string, mode string, scale int, uid string, overlay boo
7575
}
7676
defer file.Close()
7777

78-
img, err := png.Decode(file)
78+
img, err := webp.Decode(file)
7979
if err != nil {
8080
return nil, err
8181
}
8282

83-
// To prevent overload on the server, we enforce some limits on scaling.
84-
// Not smaller than 16, so that it's visible, and not more than 1024px
85-
if scale < 16 {
86-
scale = 16
87-
} else if scale > 1024 {
88-
scale = 1024
89-
}
90-
9183
switch mode {
9284
// Plain Avatar
9385
case "avatar":

utils.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import (
44
"crypto/md5"
55
"encoding/json"
66
"fmt"
7+
"github.com/chai2010/webp"
78
"github.com/google/uuid"
89
"image"
910
"image/draw"
10-
"image/png"
1111
"io"
1212
"io/ioutil"
1313
"net"
@@ -45,12 +45,17 @@ func downloadAndCacheSkin(skinURL string, cachePath string, uuid string) (string
4545
}
4646
defer file.Close()
4747

48-
// Decode the image and re-encode to PNG format
48+
// Decode the image and re-encode to WEBP format
4949
img, _, err := image.Decode(skinResp.Body)
5050
if err != nil {
5151
return "", err
5252
}
53-
png.Encode(file, img)
53+
54+
webp.Encode(file, img, &webp.Options{
55+
Lossless: true,
56+
Quality: 100,
57+
Exact: true,
58+
})
5459

5560
cacheSkin(uuid, cachePath)
5661
return cachePath, nil

0 commit comments

Comments
 (0)