feat: :construction: Support WASM as compilation target
Moves ccgo & unsupported features from other packages out into files with buildtags, and replacements in neighbouring files. Starts providing WASIp1 binaries with Goreleaser.
···88## Install
991010```sh
1111-$ go install github.com/jphastings/dotpostcard/cmd/postcards@latest
1111+go install github.com/jphastings/dotpostcard/cmd/postcards@latest
1212```
13131414-Or, if you have [Homebrew](https://brew.sh) installed:
1414+If you have [Homebrew](https://brew.sh) installed:
15151616```sh
1717-$ brew install jphastings/tools/postcards
1717+brew install jphastings/tools/postcards
1818```
1919+2020+You can also download compiled binaries from [Github releases](https://github.com/jphastings/dotpostcard/releases/) for Windows, Linux, macOS — each of which is provided for arm64 and amd64 architectures.
2121+2222+WASIp1 compatible WASM executables are also provided with a reduced feature set (notably more efficient image encoders, WebP and JPEGli, are absent).
19232024## Usage
2125
+1-1
TODO.md
···3838- [x] Decode USD & USDZ #usd
3939- [x] Creating a USD(Z) from an image that doesn't have resolution data (eg front/back portrait fixtures) seems to nil pointer fail. #bug
4040- [x] Get this CLI tool building automatically
4141-- [x] Web (webp) with transparency not convertable (eg. to USD) #bug
4141+- [x] Web (webp) with transparency not convertible (eg. to USD) #bug
+3
cmd/postcards/format.go
···11+//go:build !wasm
22+// +build !wasm
33+14package main
2536import (
···151151 usdFilename := pc.Name + extension
152152153153 // Grab the filename of the texture image, as it might be JPG or PNG
154154- webImg, _ := web.Codec("jpg", "png")
154154+ webImg, _ := web.Codec("jpeg", "png")
155155 fws, err := webImg.Encode(pc, opts)
156156 if err != nil {
157157 return nil, err
+3-3
formats/web/codec.go
···23232424type codec struct {
2525 // This holds a list of formats, the first will be tried, and if it's unsuitable, the next etc.
2626- // This is particularly useful for transparency. Eg. A `web.Codec("jpg", "webp")` would save as jpg
2626+ // This is particularly useful for transparency. Eg. A `web.Codec("jpeg", "webp")` would save as jpg
2727 // if there is no transparency, and WebP if there is transparency to encode, or if "archival" was
2828 // specified (as JPEG can't encode images losslessly).
2929 formats []string
···5454}
55555656var formatCapabilities = map[string]capabilities{
5757- "jpg": {},
5757+ "jpeg": {},
5858 "webp": {lossless: true, transparency: true},
5959 "png": {lossless: true, transparency: true},
6060}
···7777 return codec{formats: fmts}, nil
7878}
79798080-var DefaultCodec, _ = Codec("jpg", "webp")
8080+var DefaultCodec, _ = Codec("jpeg", "webp")
81818282func (c codec) Name() string { return codecName }
+5-17
formats/web/decode.go
···88 "image/jpeg"
99 "io"
10101111- "git.sr.ht/~jackmordaunt/go-libwebp/webp"
1211 "github.com/jphastings/dotpostcard/formats"
1312 "github.com/jphastings/dotpostcard/formats/xmp"
1313+ "github.com/jphastings/dotpostcard/internal/images"
1414 "github.com/jphastings/dotpostcard/pkg/xmpinject"
1515 "github.com/jphastings/dotpostcard/types"
1616)
17171818func (b bundle) Decode(decOpts formats.DecodeOptions) (types.Postcard, error) {
1919+ defer b.Close()
2020+1921 var dataCopy bytes.Buffer
2022 t := io.TeeReader(b, &dataCopy)
2123···2830 var xmpDecoder func([]byte) ([]byte, error)
2931 switch format {
3032 case "webp":
3131- imgDecoder = webp.Decode
3333+ // This function is defined in multiple files so we can keep the WebP package out of WASM builds.
3434+ imgDecoder = images.ReadWebP
3235 xmpDecoder = xmpinject.XMPfromWebP
3336 case "jpeg":
3437 imgDecoder = jpeg.Decode
···90939194 return pc, nil
9295}
9393-9494-// the goalang.org/x/image/webp decoder bugs out on Alpha layers; it gets Registered with the image package
9595-// when jackmordaunt's webp parser is loaded, but sits at the top of the pack — so using image.Decode to
9696-// decode automatically won't work (it uses golang.org version, which breaks) and using image.DecodeConfig
9797-// to get the format also fails (as that also uses the golang.org version, which breaks)
9898-// This slightly hacky approach means we can manually use only jackmordaunt's version
9999-func determineFormat(r io.Reader) (string, error) {
100100- _, err := webp.DecodeConfig(r)
101101- if err == nil {
102102- return "webp", nil
103103- }
104104-105105- _, format, err := image.Decode(r)
106106- return format, err
107107-}
···11+//go:build wasm
22+// +build wasm
33+44+package web
55+66+import (
77+ "image"
88+ "io"
99+)
1010+1111+// This is a trivial substitute for the webp enabled version that requires hackery.
1212+func determineFormat(r io.Reader) (string, error) {
1313+ _, format, err := image.Decode(r)
1414+ return format, err
1515+}
+26
formats/web/webp.go
···11+//go:build !wasm
22+// +build !wasm
33+44+package web
55+66+import (
77+ "image"
88+ "io"
99+1010+ "git.sr.ht/~jackmordaunt/go-libwebp/webp"
1111+)
1212+1313+// the goalang.org/x/image/webp decoder bugs out on Alpha layers; it gets Registered with the image package
1414+// when jackmordaunt's webp parser is loaded, but sits at the top of the pack — so using image.Decode to
1515+// decode automatically won't work (it uses golang.org version, which breaks) and using image.DecodeConfig
1616+// to get the format also fails (as that also uses the golang.org version, which breaks)
1717+// This slightly hacky approach means we can manually use only jackmordaunt's version
1818+func determineFormat(r io.Reader) (string, error) {
1919+ _, err := webp.DecodeConfig(r)
2020+ if err == nil {
2121+ return "webp", nil
2222+ }
2323+2424+ _, format, err := image.Decode(r)
2525+ return format, err
2626+}