Webp 是由谷歌在 2010 年 9 月发布的一种用于 web 端支持有损和无损压缩的图片格式

WebP 是 Google 推出的一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式。派生自影像编码格式 VP8,被认为是 WebM 多媒体格式的姊妹项目,是由 Google 在购买 On2 Technologies 后发展出来,以 BSD 授权条款发布。

WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。

浏览器对 Webp 的支持程度(https://caniuse.com/?search=webp)

下载安装

下载仓库(https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html)

已编译的WebP工具和库(适用于Linux、Windows 和 Mac OS X)

  • libwebp 库,可以用于在你的程序中添加WebP编码和解码。

  • cwebp — WebP 编码工具

  • dwebp — WebP 解码工具

  • vwebp — WebP 文件查看器

  • webpmux — WebP 复合工具

  • gif2webp — 将 GIF 图像转换为 WebP 的工具

使用 cwebp 进行压缩

使用方法如下:

1
cwebp [options] input_file -o output_file.webp

其中 options 是压缩参数配置,包含是否启用无损压缩 -lossless ,压缩系数 -q(0~100) 等,如使用 80 的压缩系数对目标文件进行有损压缩:

1
cwebp -q 80 image.png -o image.webp

converts a GIF image to a WebP image.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package main

import (
    "bytes"
    "io"
    "log"
    "mime/multipart"
    "net/http"
    "os/exec"
    "strconv"
    "strings"
)

const (
    B  uint64 = 1
    KB uint64 = 1 << (10 * iota)
    MB
    GB
    TB
    PB
    EB
)

const maxSize = 24 * KB

func main() {
    log.Printf("Start gif2web server on 8080")
    http.Handle("/convert", gifProcessor())
    http.ListenAndServe("0.0.0.0:8080", nil)
}

func gifProcessor() http.HandlerFunc {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        if err := req.ParseMultipartForm(maxSize); nil != err {
            log.Printf("Error while parse: %s", err)
            w.WriteHeader(http.StatusInternalServerError)
            return
        }
        for _, fheaders := range req.MultipartForm.File {
            for _, hdr := range fheaders {
                log.Printf("Income file len: %d", hdr.Size)

                var err error
                var infile multipart.File
                if infile, err = hdr.Open(); err != nil {
                    log.Printf("[ERROR] Handle open error: %v", err)
                    w.WriteHeader(http.StatusInternalServerError)
                    return
                }
                quality := req.URL.Query().Get("quality")
                if quality == "" {
                    quality = "0"
                }
                q, err := strconv.ParseInt(quality, 10, 64)
                if err != nil || q < 0 || q > 100 {
                    log.Printf("[ERROR] Bad quality params: %s %v", err, q)
                    w.WriteHeader(http.StatusInternalServerError)
                    return
                }
                cmd := exec.Command("gif2webp", "-mixed", "-q", strconv.Itoa(int(q)), "-o", "-", "--", "-")

                cmd.Stdin = io.Reader(infile)

                var out bytes.Buffer
                cmd.Stdout = &out

                var errout bytes.Buffer
                cmd.Stderr = &errout

                err = cmd.Run()
                if err != nil {
                    log.Printf("[ERROR] Webp Output: %v, %v, %v\n", err, out.String(), errout.String())
                    w.WriteHeader(http.StatusInternalServerError)
                    return
                }
                output := out.String()
                reader := strings.NewReader(output)
                imageLen := reader.Len()
                log.Printf("Outcome file len: %d", reader.Len())
                w.Header().Set("Content-Type", "image/webp")
                w.Header().Set("Content-Length", strconv.Itoa(imageLen))
                w.WriteHeader(http.StatusOK)
                io.Copy(w, reader)
                return
            }
        }

        w.WriteHeader(http.StatusInternalServerError)
    })
}

WebP decoder and encoder for Go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "log"

    "github.com/chai2010/webp"
)

func main() {
    var buf bytes.Buffer
    var width, height int
    var data []byte
    var err error

    // Load file data
    if data, err = ioutil.ReadFile("./testdata/1_webp_ll.webp"); err != nil {
        log.Println(err)
    }

    // GetInfo
    if width, height, _, err = webp.GetInfo(data); err != nil {
        log.Println(err)
    }
    fmt.Printf("width = %d, height = %d\n", width, height)

    // GetMetadata
    if metadata, err := webp.GetMetadata(data, "ICCP"); err != nil {
        fmt.Printf("Metadata: err = %v\n", err)
    } else {
        fmt.Printf("Metadata: %s\n", string(metadata))
    }

    // Decode webp
    m, err := webp.Decode(bytes.NewReader(data))
    if err != nil {
        log.Println(err)
    }

    // Encode lossless webp
    if err = webp.Encode(&buf, m, &webp.Options{Lossless: true}); err != nil {
        log.Println(err)
    }
    if err = ioutil.WriteFile("output.webp", buf.Bytes(), 0666); err != nil {
        log.Println(err)
    }
    
    fmt.Println("Save output.webp ok")
}

参考