Skip to content

Commit

Permalink
Extract drawing operations to Canvas interface (#1019)
Browse files Browse the repository at this point in the history
* Revert "fonts: fix race condition (#963)"

This reverts commit 58155a8.

* Extract drawing operations to Canvas interface

* Add TTF versions of all fonts

* Fix buildifier errors by upgrading to latest

* Make DrawStringWrapped more generic

Move implementation details into `GGCanvas`.
  • Loading branch information
rohansingh authored Mar 28, 2024
1 parent 7465b35 commit 65f1eb0
Show file tree
Hide file tree
Showing 58 changed files with 651 additions and 438 deletions.
11 changes: 5 additions & 6 deletions cmd/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"image"
"io/ioutil"
"os"
"strings"
"time"
Expand All @@ -13,7 +12,7 @@ import (
"go.starlark.net/starlark"

"tidbyt.dev/pixlet/encode"
"tidbyt.dev/pixlet/globals"
pixletrender "tidbyt.dev/pixlet/render"
"tidbyt.dev/pixlet/runtime"
"tidbyt.dev/pixlet/starlarkutil"
)
Expand Down Expand Up @@ -80,8 +79,8 @@ var RenderCmd = &cobra.Command{
func render(cmd *cobra.Command, args []string) error {
script := args[0]

globals.Width = width
globals.Height = height
pixletrender.FrameWidth = width
pixletrender.FrameHeight = height

if !strings.HasSuffix(script, ".star") {
return fmt.Errorf("script file must have suffix .star: %s", script)
Expand All @@ -106,7 +105,7 @@ func render(cmd *cobra.Command, args []string) error {
config[split[0]] = strings.Join(split[1:], "=")
}

src, err := ioutil.ReadFile(script)
src, err := os.ReadFile(script)
if err != nil {
return fmt.Errorf("failed to read file %s: %w", script, err)
}
Expand Down Expand Up @@ -200,7 +199,7 @@ func render(cmd *cobra.Command, args []string) error {
if outPath == "-" {
_, err = os.Stdout.Write(buf)
} else {
err = ioutil.WriteFile(outPath, buf, 0644)
err = os.WriteFile(outPath, buf, 0644)
}

if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion docs/schema/location/example.star
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
load("encoding/json.star", "json")
load("render.star", "render")
load("schema.star", "schema")
load("encoding/json.star", "json")

DEFAULT_LOCATION = """
{
Expand Down
2 changes: 1 addition & 1 deletion docs/schema/locationbased/example.star
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
load("encoding/json.star", "json")
load("render.star", "render")
load("schema.star", "schema")
load("encoding/json.star", "json")

EXAMPLE_LOCATION = """
{
Expand Down
2 changes: 1 addition & 1 deletion docs/schema/oauth2/example.star
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
load("encoding/json.star", "json")
load("http.star", "http")
load("render.star", "render")
load("schema.star", "schema")
load("secret.star", "secret")
load("encoding/json.star", "json")

OAUTH2_CLIENT_SECRET = secret.decrypt("your-client-secret")

Expand Down
2 changes: 1 addition & 1 deletion docs/schema/photoselect/example.star
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
load("encoding/base64.star", "base64")
load("render.star", "render")
load("schema.star", "schema")
load("encoding/base64.star", "base64")

def main(config):
encoded = config.get("photo", DEFAULT_PHOTO)
Expand Down
4 changes: 2 additions & 2 deletions examples/bitcoin.star
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
load("render.star", "render")
load("http.star", "http")
load("encoding/base64.star", "base64")
load("http.star", "http")
load("render.star", "render")

COINDESK_PRICE_URL = "https://api.coindesk.com/v1/bpi/currentprice.json"

Expand Down
2 changes: 1 addition & 1 deletion examples/humanize.star
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
load("humanize.star", "humanize")
load("render.star", "render")
load("schema.star", "schema")
load("time.star", "time")
load("humanize.star", "humanize")

DEFAULT_COUNTER = "1337"
DEFAULT_APPS = "42"
Expand Down
2 changes: 1 addition & 1 deletion examples/qrcode.star
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
load("cache.star", "cache")
load("encoding/base64.star", "base64")
load("render.star", "render")
load("qrcode.star", "qrcode")
load("render.star", "render")

def main(config):
url = "https://tidbyt.com?utm_source=pixlet_example"
Expand Down
4 changes: 2 additions & 2 deletions examples/sunrise.star
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
load("encoding/json.star", "json")
load("render.star", "render")
load("schema.star", "schema")
load("time.star", "time")
load("encoding/json.star", "json")
load("sunrise.star", "sunrise")
load("time.star", "time")

DEFAULT_LOCATION = """
{
Expand Down
Binary file added fonts/10x20.ttf
Binary file not shown.
Binary file added fonts/5x8.ttf
Binary file not shown.
Binary file added fonts/6x13.ttf
Binary file not shown.
Binary file added fonts/CG-pixel-3x5-mono.ttf
Binary file not shown.
Binary file added fonts/CG-pixel-4x5-mono.ttf
Binary file not shown.
Binary file added fonts/Dina_r400-6.ttf
Binary file not shown.
91 changes: 91 additions & 0 deletions fonts/fonts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package fonts

import (
"embed"
"fmt"
"log"
"strings"
"sync"

"github.com/zachomedia/go-bdf"
)

var (
//go:embed *.bdf *.ttf
files embed.FS

once sync.Once
fontCache = map[string]*Font{}
)

type Font struct {
Name string
Font *bdf.Font

// BDF is the raw BDF font data.
BDF []byte

// TTF is the raw TTF font data.
TTF []byte
}

func loadFonts() {
fontFileInfos, err := files.ReadDir(".")
if err != nil {
fmt.Println("files.ReadDir()", err)
return
}

for _, ffi := range fontFileInfos {
if !strings.HasSuffix(ffi.Name(), ".bdf") {
continue
}

name := strings.TrimSuffix(ffi.Name(), ".bdf")

bdfBuf, err := files.ReadFile(name + ".bdf")
if err != nil {
fmt.Printf("files.ReadFile(): %s\n", err)
continue
}

ttfBuf, err := files.ReadFile(name + ".ttf")
if err != nil {
fmt.Printf("files.ReadFile(): %s\n", err)
continue
}

fnt, err := bdf.Parse(bdfBuf)
if err != nil {
fmt.Printf("bdf.Parse(%s): %s\n", ffi.Name(), err)
}

fontCache[name] = &Font{
Name: name,
Font: fnt,
BDF: bdfBuf,
TTF: ttfBuf,
}
}
}

func Names() []string {
once.Do(loadFonts)

fontNames := []string{}
for key := range fontCache {
fontNames = append(fontNames, key)
}
return fontNames
}

func GetFont(name string) *Font {
once.Do(loadFonts)

font, ok := fontCache[name]
if !ok {
log.Panicf("Unknown font '%s', the available fonts are: %v", name, Names())
}

return font
}
Binary file added fonts/tb-8.ttf
Binary file not shown.
Binary file added fonts/tom-thumb.ttf
Binary file not shown.
4 changes: 0 additions & 4 deletions globals/global.go

This file was deleted.

20 changes: 11 additions & 9 deletions render/animation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package render
import (
"image"

"github.com/tidbyt/gg"
"tidbyt.dev/pixlet/render/canvas"
)

// Animations turns a list of children into an animation, where each
Expand All @@ -16,13 +16,15 @@ import (
//
// EXAMPLE BEGIN
// render.Animation(
// children=[
// render.Box(width=10, height=10, color="#300"),
// render.Box(width=12, height=12, color="#500"),
// render.Box(width=14, height=14, color="#700"),
// render.Box(width=16, height=16, color="#900"),
// render.Box(width=18, height=18, color="#b00"),
// ],
//
// children=[
// render.Box(width=10, height=10, color="#300"),
// render.Box(width=12, height=12, color="#500"),
// render.Box(width=14, height=14, color="#700"),
// render.Box(width=16, height=16, color="#900"),
// render.Box(width=18, height=18, color="#b00"),
// ],
//
// )
// EXAMPLE END
type Animation struct {
Expand All @@ -49,7 +51,7 @@ func (a Animation) PaintBounds(bounds image.Rectangle, frameIdx int) image.Recta
return a.Children[ModInt(frameIdx, len(a.Children))].PaintBounds(bounds, frameIdx)
}

func (a Animation) Paint(dc *gg.Context, bounds image.Rectangle, frameIdx int) {
func (a Animation) Paint(dc canvas.Canvas, bounds image.Rectangle, frameIdx int) {
if len(a.Children) == 0 {
return
}
Expand Down
6 changes: 2 additions & 4 deletions render/animation/positioned.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import (
"image"
"math"

"github.com/tidbyt/gg"

"tidbyt.dev/pixlet/render"
"tidbyt.dev/pixlet/render/canvas"
)

// Animate a widget from start to end coordinates.
Expand All @@ -22,7 +21,6 @@ import (
// DOC(Curve): Easing curve to use, default is 'linear'
// DOC(Delay): Delay before animation in frames
// DOC(Hold): Delay after animation in frames
//
type AnimatedPositioned struct {
render.Widget
Child render.Widget `starlark:"child,required"`
Expand All @@ -40,7 +38,7 @@ func (o AnimatedPositioned) PaintBounds(bounds image.Rectangle, frameIdx int) im
return bounds
}

func (o AnimatedPositioned) Paint(dc *gg.Context, bounds image.Rectangle, frameIdx int) {
func (o AnimatedPositioned) Paint(dc canvas.Canvas, bounds image.Rectangle, frameIdx int) {
var position float64

if frameIdx < o.Delay {
Expand Down
11 changes: 7 additions & 4 deletions render/animation/rotate.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package animation

import (
"github.com/tidbyt/gg"
"math"

"tidbyt.dev/pixlet/render/canvas"
)

// Transform by rotating by a given angle in degrees.
//
// DOC(Angle): Angle to rotate by in degrees
//
type Rotate struct {
Angle float64 `starlark:"angle,required"`
}

func (self Rotate) Apply(ctx *gg.Context, origin Vec2f, rounding Rounding) {
ctx.RotateAbout(gg.Radians(self.Angle), origin.X, origin.Y)
func (self Rotate) Apply(ctx canvas.Canvas, origin Vec2f, rounding Rounding) {
ctx.Translate(origin.X, origin.Y)
ctx.Rotate(self.Angle * math.Pi / 180)
ctx.Translate(-origin.X, -origin.Y)
}

func (self Rotate) Interpolate(other Transform, progress float64) (result Transform, ok bool) {
Expand Down
11 changes: 5 additions & 6 deletions render/animation/scale.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package animation

import (
"github.com/tidbyt/gg"
)
import "tidbyt.dev/pixlet/render/canvas"

// Transform by scaling by a given factor.
//
// DOC(X): Horizontal scale factor
// DOC(Y): Vertical scale factor
//
type Scale struct {
Vec2f
}

func (self Scale) Apply(ctx *gg.Context, origin Vec2f, rounding Rounding) {
ctx.ScaleAbout(self.X, self.Y, origin.X, origin.Y)
func (self Scale) Apply(ctx canvas.Canvas, origin Vec2f, rounding Rounding) {
ctx.Translate(origin.X, origin.Y)
ctx.Scale(self.X, self.Y)
ctx.Translate(-origin.X, -origin.Y)
}

func (self Scale) Interpolate(other Transform, progress float64) (result Transform, ok bool) {
Expand Down
4 changes: 2 additions & 2 deletions render/animation/transform.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package animation

import (
"github.com/tidbyt/gg"
"tidbyt.dev/pixlet/render/canvas"
)

type Transform interface {
Apply(ctx *gg.Context, origin Vec2f, rounding Rounding)
Apply(ctx canvas.Canvas, origin Vec2f, rounding Rounding)
Interpolate(other Transform, progress float64) (result Transform, ok bool)
}

Expand Down
Loading

0 comments on commit 65f1eb0

Please sign in to comment.