From 95c273d0b818f2953b5ea0326efd31c747ae3168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A1=9C=E8=8F=AF?= Date: Thu, 6 Apr 2023 12:37:38 +0800 Subject: [PATCH] init --- .gitignore | 1 + bin/main.go | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 12 +++++++ go.sum | 13 +++++++ webp.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 214 insertions(+) create mode 100644 bin/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 webp.go diff --git a/.gitignore b/.gitignore index adf8f72..a8e3346 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ # Go workspace file go.work +data/ diff --git a/bin/main.go b/bin/main.go new file mode 100644 index 0000000..b2f4c63 --- /dev/null +++ b/bin/main.go @@ -0,0 +1,99 @@ +package main + +import ( + "image" + "image/gif" + "log" + "net/http" + "os" + + "regexp" + "strconv" + + "github.com/chai2010/webp" + "github.com/disintegration/imaging" +) + +// 实现一个 web api 服务(获取指定尺寸的图片) +func main() { + + http.HandleFunc("/img/", func(w http.ResponseWriter, r *http.Request) { + + // URL 格式: /img/{id}-{version}@{倍图}{宽度}.{格式} + reg := regexp.MustCompile(`^/img/([0-9a-zA-Z]+)-([0-9a-zA-Z]+)@([0-9]{1,4})x([0-9]{1,4}).(jpg|jpeg|png|webp)$`) + matches := reg.FindStringSubmatch(r.URL.Path) + if len(matches) != 6 { + log.Println("URL 格式错误", matches) + w.WriteHeader(http.StatusBadRequest) + return + } + + // 正则表达式获取参数 + id, version, multiple_str, width_str, format := matches[1], matches[2], matches[3], matches[4], matches[5] + multiple, err := strconv.Atoi(multiple_str) + if err != nil { + log.Println("倍图参数错误", multiple_str) + w.WriteHeader(http.StatusBadRequest) + return + } + + width, err := strconv.Atoi(width_str) + if err != nil { + log.Println("宽度参数错误", width_str) + w.WriteHeader(http.StatusBadRequest) + return + } + + log.Println(id, version, multiple, width, format) + + // 打開輸入文件 + file, err := os.Open("data/test.gif") + if err != nil { + log.Println("打开文件失败", err) + w.WriteHeader(http.StatusBadRequest) + return + } + defer file.Close() + + // 將輸入文件解碼為 image.Image + img, ext, err := image.Decode(file) + if err != nil { + log.Println("解码图像失败", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + // 如果是 GIF 格式的动态图片,直接返回 + if ext == "gif" { + giffile, err := os.Open("data/test.gif") + if err != nil { + log.Println("打开文件失败", err) + w.WriteHeader(http.StatusBadRequest) + return + } + defer giffile.Close() + gifimg, err := gif.DecodeAll(giffile) + if err != nil { + log.Println("解码图像失败", err) + w.WriteHeader(http.StatusBadRequest) + return + } + gif.EncodeAll(w, gifimg) + return + } + + // 將圖像轉換為 RGBA 格式(如果尚未採用該格式) + if _, ok := img.(*image.RGBA); !ok { + img = imaging.Clone(img) + } + + // 將圖像調整為指定宽度的 WebP 格式並將其寫入輸出到瀏覽器(高度自适应) + if err = webp.Encode(w, imaging.Resize(img, multiple*width, 0, imaging.Lanczos), &webp.Options{Quality: 80}); err != nil { + log.Println("编码图像失败", err.Error()) + w.WriteHeader(http.StatusBadRequest) + return + } + }) + log.Println("Server is running at http://localhost:6001") + http.ListenAndServe(":6001", nil) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8bbe140 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module git.satori.love/gameui/webp + +go 1.18 + +require github.com/disintegration/imaging v1.6.2 + +require github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect + +require ( + github.com/chai2010/webp v1.1.1 + golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b005707 --- /dev/null +++ b/go.sum @@ -0,0 +1,13 @@ +github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk= +github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 h1:A7o8tOERTtpD/poS+2VoassCjXpjHn916luXbf5QKD0= +github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882/go.mod h1:5IwJoz9Pw7JsrCN4/skkxUtSWT7myuUPLhCgv6Q5vvQ= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +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/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= diff --git a/webp.go b/webp.go new file mode 100644 index 0000000..b5f9b25 --- /dev/null +++ b/webp.go @@ -0,0 +1,89 @@ +package webp + +import ( + "image" + "os" + + "github.com/chai2010/webp" + "github.com/disintegration/imaging" +) + +// EncodeFile encodes the specified image file to WebP format with the given width and height. +// The output file name is constructed by adding the .webp extension to the input file name. +func EncodeFile(filename string, width, height int) error { + // Open the input file + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + // Decode the input file into an image.Image + img, format, err := image.Decode(file) + if err != nil { + return err + } + + // Convert the image to RGBA format if it's not already in that format + if _, ok := img.(*image.RGBA); !ok { + img = imaging.Clone(img) + } + + // Resize the image to the specified dimensions + resizedImg := imaging.Resize(img, width, height, imaging.Lanczos) + + // Create a new file for writing the output + outFilename := filename[:len(filename)-len(format)] + "webp" + outFile, err := os.Create(outFilename) + if err != nil { + return err + } + defer outFile.Close() + + // Encode the resized image to WebP format and write it to the output file + err = webp.Encode(outFile, resizedImg, &webp.Options{Quality: 80}) + if err != nil { + return err + } + + return nil +} + +//// Encode encodes the specified image to WebP format with the given width and height. +//// The output data is returned as a byte slice. +//func Encode(data []byte, width, height int) ([]byte, error) { +// // Decode the input data into an image.Image +// img, format, err := image.Decode(ioutil.NopCloser(bytes.NewReader(data))) +// if err != nil { +// return nil, err +// } +// +// // Convert the image to RGBA format if it's not already in that format +// if _, ok := img.(*image.RGBA); !ok { +// img = imaging.Clone(img) +// } +// +// // Resize the image to the specified dimensions +// resizedImg := imaging.Resize(img, width, height, imaging.Lanczos) +// +// // Encode the resized image to WebP format and return it as a byte slice +// var buf bytes.Buffer +// err = webp.Encode(&buf, resizedImg, &webp.Options{Quality: 80}) +// if err != nil { +// return nil, err +// } +// +// return buf.Bytes(), nil +//} +// +//// Decode decodes the specified WebP data into an image.Image. +//func Decode(data []byte) (image.Image, error) { +// return webp.Decode(bytes.NewReader(data)) +//} +// +//// IsWebP returns true if the specified data is a WebP image. +//func IsWebP(data []byte) bool { +// return webp.IsWebP(data) +//} +// +//// IsPNG returns true if the specified