Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a pure-browser version of the Pixlet UI #784

Merged
merged 2 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ examples/*.gif
# Pixlet Binary
pixlet
pixlet.exe
pixlet.wasm

# Releases
build/
out/
dist/

# Dependency directories
node_modules
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ else
TAGS =
endif

all: build
all: build wasm

test:
go test $(TAGS) -v -cover ./...
Expand All @@ -24,7 +24,7 @@ bench:
go test -benchmem -benchtime=20s -bench BenchmarkRunAndRender tidbyt.dev/pixlet/encode

build:
go build $(LDFLAGS) $(TAGS) -o $(BINARY) tidbyt.dev/pixlet
go build $(LDFLAGS) $(TAGS) -o $(BINARY) tidbyt.dev/pixlet

embedfonts:
go run render/gen/embedfonts.go
Expand All @@ -50,4 +50,7 @@ lint:
@ buildifier --version >/dev/null 2>&1 || $(MAKE) install-buildifier
buildifier -r ./

format: lint
format: lint

wasm:
GOOS=js GOARCH=wasm go build -o ./src/pixlet.wasm tidbyt.dev/pixlet
1 change: 0 additions & 1 deletion cmd/community/community.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
)

func init() {
CommunityCmd.AddCommand(CreateManifestCmd)
CommunityCmd.AddCommand(ListIconsCmd)
CommunityCmd.AddCommand(LoadAppCmd)
CommunityCmd.AddCommand(SpellCheckCmd)
Expand Down
6 changes: 6 additions & 0 deletions cmd/community/createmanifest.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js && !wasm

package community

import (
Expand All @@ -18,6 +20,10 @@ var CreateManifestCmd = &cobra.Command{
RunE: CreateManifest,
}

func init() {
CommunityCmd.AddCommand(CreateManifestCmd)
}

func CreateManifest(cmd *cobra.Command, args []string) error {
fileName := filepath.Base(args[0])
if fileName != manifest.ManifestFileName {
Expand Down
2 changes: 2 additions & 0 deletions cmd/community/manifestprompt.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js && !wasm

package community

import (
Expand Down
2 changes: 2 additions & 0 deletions cmd/create.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js && !wasm

package cmd

import (
Expand Down
110 changes: 0 additions & 110 deletions encode/encode.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
package encode

import (
"bytes"
"crypto/sha256"
"fmt"
"image"
"image/color"
"image/draw"
"image/gif"
"time"

"github.com/ericpauley/go-quantize/quantize"
"github.com/pkg/errors"
"github.com/tidbyt/go-libwebp/webp"
"github.com/vmihailenco/msgpack/v5"

"tidbyt.dev/pixlet/render"
Expand Down Expand Up @@ -92,108 +84,6 @@ func (s *Screens) Hash() ([]byte, error) {
return h[:], nil
}

// Renders a screen to WebP. Optionally pass filters for
// postprocessing each individual frame.
func (s *Screens) EncodeWebP(maxDuration int, filters ...ImageFilter) ([]byte, error) {
images, err := s.render(filters...)
if err != nil {
return nil, err
}

if len(images) == 0 {
return []byte{}, nil
}

bounds := images[0].Bounds()
anim, err := webp.NewAnimationEncoder(
bounds.Dx(),
bounds.Dy(),
WebPKMin,
WebPKMax,
)
if err != nil {
return nil, errors.Wrap(err, "initializing encoder")
}
defer anim.Close()

remainingDuration := time.Duration(maxDuration) * time.Millisecond
for _, im := range images {
frameDuration := time.Duration(s.delay) * time.Millisecond

if maxDuration > 0 {
if frameDuration > remainingDuration {
frameDuration = remainingDuration
}
remainingDuration -= frameDuration
}

if err := anim.AddFrame(im, frameDuration); err != nil {
return nil, errors.Wrap(err, "adding frame")
}

if maxDuration > 0 && remainingDuration <= 0 {
break
}
}

buf, err := anim.Assemble()
if err != nil {
return nil, errors.Wrap(err, "encoding animation")
}

return buf, nil
}

// Renders a screen to GIF. Optionally pass filters for postprocessing
// each individual frame.
func (s *Screens) EncodeGIF(maxDuration int, filters ...ImageFilter) ([]byte, error) {
images, err := s.render(filters...)
if err != nil {
return nil, err
}

if len(images) == 0 {
return []byte{}, nil
}

g := &gif.GIF{}

remainingDuration := maxDuration
for imIdx, im := range images {
imRGBA, ok := im.(*image.RGBA)
if !ok {
return nil, fmt.Errorf("image %d is %T, require RGBA", imIdx, im)
}

palette := quantize.MedianCutQuantizer{}.Quantize(make([]color.Color, 0, 256), im)
imPaletted := image.NewPaletted(imRGBA.Bounds(), palette)
draw.Draw(imPaletted, imRGBA.Bounds(), imRGBA, image.Point{0, 0}, draw.Src)

frameDelay := int(s.delay)
if maxDuration > 0 {
if frameDelay > remainingDuration {
frameDelay = remainingDuration
}
remainingDuration -= frameDelay
}

g.Image = append(g.Image, imPaletted)
g.Delay = append(g.Delay, frameDelay/10) // in 100ths of a second

if maxDuration > 0 && remainingDuration <= 0 {
break
}
}

buf := &bytes.Buffer{}
err = gif.EncodeAll(buf, g)
if err != nil {
return nil, errors.Wrap(err, "encoding")
}

return buf.Bytes(), nil
}

func (s *Screens) render(filters ...ImageFilter) ([]image.Image, error) {
if s.images == nil {
s.images = render.PaintRoots(true, s.roots...)
Expand Down
63 changes: 63 additions & 0 deletions encode/gif.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package encode

import (
"bytes"
"fmt"
"image"
"image/color"
"image/draw"
"image/gif"

"github.com/ericpauley/go-quantize/quantize"
"github.com/pkg/errors"
)

// Renders a screen to GIF. Optionally pass filters for postprocessing
// each individual frame.
func (s *Screens) EncodeGIF(maxDuration int, filters ...ImageFilter) ([]byte, error) {
images, err := s.render(filters...)
if err != nil {
return nil, err
}

if len(images) == 0 {
return []byte{}, nil
}

g := &gif.GIF{}

remainingDuration := maxDuration
for imIdx, im := range images {
imRGBA, ok := im.(*image.RGBA)
if !ok {
return nil, fmt.Errorf("image %d is %T, require RGBA", imIdx, im)
}

palette := quantize.MedianCutQuantizer{}.Quantize(make([]color.Color, 0, 256), im)
imPaletted := image.NewPaletted(imRGBA.Bounds(), palette)
draw.Draw(imPaletted, imRGBA.Bounds(), imRGBA, image.Point{0, 0}, draw.Src)

frameDelay := int(s.delay)
if maxDuration > 0 {
if frameDelay > remainingDuration {
frameDelay = remainingDuration
}
remainingDuration -= frameDelay
}

g.Image = append(g.Image, imPaletted)
g.Delay = append(g.Delay, frameDelay/10) // in 100ths of a second

if maxDuration > 0 && remainingDuration <= 0 {
break
}
}

buf := &bytes.Buffer{}
err = gif.EncodeAll(buf, g)
if err != nil {
return nil, errors.Wrap(err, "encoding")
}

return buf.Bytes(), nil
}
62 changes: 62 additions & 0 deletions encode/webp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//go:build !js && !wasm

package encode

import (
"time"

"github.com/pkg/errors"
"github.com/tidbyt/go-libwebp/webp"
)

// Renders a screen to WebP. Optionally pass filters for
// postprocessing each individual frame.
func (s *Screens) EncodeWebP(maxDuration int, filters ...ImageFilter) ([]byte, error) {
images, err := s.render(filters...)
if err != nil {
return nil, err
}

if len(images) == 0 {
return []byte{}, nil
}

bounds := images[0].Bounds()
anim, err := webp.NewAnimationEncoder(
bounds.Dx(),
bounds.Dy(),
WebPKMin,
WebPKMax,
)
if err != nil {
return nil, errors.Wrap(err, "initializing encoder")
}
defer anim.Close()

remainingDuration := time.Duration(maxDuration) * time.Millisecond
for _, im := range images {
frameDuration := time.Duration(s.delay) * time.Millisecond

if maxDuration > 0 {
if frameDuration > remainingDuration {
frameDuration = remainingDuration
}
remainingDuration -= frameDuration
}

if err := anim.AddFrame(im, frameDuration); err != nil {
return nil, errors.Wrap(err, "adding frame")
}

if maxDuration > 0 && remainingDuration <= 0 {
break
}
}

buf, err := anim.Assemble()
if err != nil {
return nil, errors.Wrap(err, "encoding animation")
}

return buf, nil
}
10 changes: 10 additions & 0 deletions encode/webp_js.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//go:build js && wasm

package encode

// Renders a screen to WebP. Optionally pass filters for
// postprocessing each individual frame.
func (s *Screens) EncodeWebP(maxDuration int, filters ...ImageFilter) ([]byte, error) {
// lol you gullible sucker, you thought you could use webp in wasm?
return s.EncodeGIF(maxDuration, filters...)
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ require (
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/nlepage/go-js-promise v1.0.0 // indirect
github.com/nlepage/go-wasm-http-server v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,10 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c h1:4RYnE0ISVwRxm9Dfo7utw1dh0kdRDEmVYq2MFVLy5zI=
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c/go.mod h1:DvuJJ/w1Y59rG8UTDxsMk5U+UJXJwuvUgbiJSm9yhX8=
github.com/nlepage/go-js-promise v1.0.0 h1:K7OmJ3+0BgWJ2LfXchg2sI6RDr7AW/KWR8182epFwGQ=
github.com/nlepage/go-js-promise v1.0.0/go.mod h1:bdOP0wObXu34euibyK39K1hoBCtlgTKXGc56AGflaRo=
github.com/nlepage/go-wasm-http-server v1.1.0 h1:phw2NtSp71m/6NmGjE2veQ41PBPzWFcnE614cKucy5M=
github.com/nlepage/go-wasm-http-server v1.1.0/go.mod h1:xpffUeN97vuv8CTlMJ2oC5tPsftfPoG9HkAgI9gkiPI=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/paulmach/orb v0.1.5/go.mod h1:pPwxxs3zoAyosNSbNKn1jiXV2+oovRDObDKfTvRegDI=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
Expand Down
1 change: 0 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ func init() {
rootCmd.AddCommand(cmd.DevicesCmd)
rootCmd.AddCommand(cmd.FormatCmd)
rootCmd.AddCommand(cmd.LintCmd)
rootCmd.AddCommand(cmd.CreateCmd)
rootCmd.AddCommand(cmd.CheckCmd)
rootCmd.AddCommand(cmd.BundleCmd)
rootCmd.AddCommand(cmd.UploadCmd)
Expand Down
11 changes: 11 additions & 0 deletions main_nonjs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build !js && !wasm

package main

import (
"tidbyt.dev/pixlet/cmd"
)

func init() {
rootCmd.AddCommand(cmd.CreateCmd)
}
Loading