summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2026-07-11 13:09:00 -0230
committerSam Anthony <sam@samanthony.xyz>2026-07-11 13:09:00 -0230
commit6c1f9200881b859bcc6d7d5b0d9d37a5cc65eb60 (patch)
tree7095e5f52f3a62b08135810e59f9a233b861fc4e
downloadhtmlimg-6c1f9200881b859bcc6d7d5b0d9d37a5cc65eb60.zip
encode
-rw-r--r--go.mod11
-rw-r--r--go.sum10
-rw-r--r--htmlimg.go23
-rw-r--r--htmlimg_test.go73
4 files changed, 117 insertions, 0 deletions
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..9137d60
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,11 @@
+module git.samanthony.xyz/htmlimg
+
+go 1.23
+
+require github.com/stretchr/testify v1.11.1
+
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..c4c1710
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,10 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/htmlimg.go b/htmlimg.go
new file mode 100644
index 0000000..332bc69
--- /dev/null
+++ b/htmlimg.go
@@ -0,0 +1,23 @@
+// Package htmlimg produces base64-encoded HTML <img> tags.
+//
+// <img src="data:image/png;base64,iVBORw...">
+package htmlimg
+
+import (
+ "bytes"
+ "encoding/base64"
+ "html/template"
+ "image"
+ "image/png"
+)
+
+// Encode PNG- and then base64-encodes img and returns it embedded in an
+// HTML <img> tag.
+func Encode(m image.Image) (template.HTML, error) {
+ pngBuf := new(bytes.Buffer)
+ if err := png.Encode(pngBuf, m); err != nil {
+ return "", err
+ }
+ b64 := base64.StdEncoding.EncodeToString(pngBuf.Bytes())
+ return template.HTML(`<img src="data:image/png;base64,` + b64 + `">`), nil
+}
diff --git a/htmlimg_test.go b/htmlimg_test.go
new file mode 100644
index 0000000..a3fa857
--- /dev/null
+++ b/htmlimg_test.go
@@ -0,0 +1,73 @@
+package htmlimg_test
+
+import (
+ "bytes"
+ "encoding/base64"
+ "image"
+ "image/color"
+ "image/png"
+ "math/rand/v2"
+ "regexp"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "git.samanthony.xyz/htmlimg"
+)
+
+var tagExpr = regexp.MustCompile(`^<img src="data:image/png;base64,([A-Za-z0-9+/=]+)">$`)
+
+func TestOutputMatchRegexp(t *testing.T) {
+ t.Parallel()
+ img := newImg(16)
+ tag, err := htmlimg.Encode(img)
+ require.NoError(t, err)
+ require.Regexp(t, tagExpr, tag)
+}
+
+func TestDecodeImg(t *testing.T) {
+ t.Parallel()
+
+ img := newImg(16)
+
+ // Encode <img> tag
+ tag, err := htmlimg.Encode(img)
+ require.NoError(t, err)
+ t.Log(tag)
+
+ // Extract and decode base64 data from tag to get png data
+ groups := tagExpr.FindStringSubmatch(string(tag))
+ require.Len(t, groups, 2)
+ b64 := groups[1]
+ decPng, err := base64.StdEncoding.DecodeString(b64)
+ require.NoError(t, err)
+
+ // Decoded png should match original image's png
+ encPng := new(bytes.Buffer)
+ err = png.Encode(encPng, img)
+ require.NoError(t, err)
+ require.Equal(t, encPng.Bytes(), decPng)
+}
+
+func BenchmarkEncode(b *testing.B) {
+ img := newImg(1024)
+ for b.Loop() {
+ htmlimg.Encode(img)
+ }
+}
+
+func newImg(size int) image.Image {
+ w, h := size, size
+ img := image.NewNRGBA(image.Rect(0, 0, w, h))
+ for y := 0; y < h; y++ {
+ for x := 0; x < w; x++ {
+ img.Set(x, y, color.NRGBA{
+ R: uint8(rand.IntN(256)),
+ G: uint8(rand.IntN(256)),
+ B: uint8(rand.IntN(256)),
+ A: uint8(rand.IntN(256)),
+ })
+ }
+ }
+ return img
+}