A CONTRIBUTING.md => CONTRIBUTING.md +31 -0
@@ 0,0 1,31 @@
+## Submitting Patches
+
+To submit a patch, first learn to use `git send-email` by reading
+[git-send-email.io], then read the SourceHut [mailing list etiquette] guide.
+You can send patches to my general purpose patches [mailing list].
+
+Please prefix the subject with `[PATCH avatar]`.
+To configure your checkout of this repo to always use the correct prefix and
+send to the correct list cd into the repo and run:
+
+ git config sendemail.to ~samwhited/patches@lists.sr.ht
+ git config format.subjectPrefix 'PATCH avatar'
+
+[git-send-email.io]: https://git-send-email.io/
+[mailing list etiquette]: https://man.sr.ht/lists.sr.ht/etiquette.md
+[mailing list]: https://lists.sr.ht/~samwhited/patches
+
+
+## License
+
+Licensed under the BSD 2 Clause License ([LICENSE] or
+https://opensource.org/licenses/BSD-2-Clause)
+
+[LICENSE]: https://git.sr.ht/~samwhited/avatar/tree/master/LICENSE.md
+
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you shall be licensed as above, without any
+additional terms or conditions.
A LICENSE => LICENSE +23 -0
@@ 0,0 1,23 @@
+Copyright © 2014 The Soquee Contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
A README.md => README.md +21 -0
@@ 0,0 1,21 @@
+# testlog
+
+Package **`avatar`** contains functions for creating default user avatars.
+
+```go
+import (
+ "code.soquee.net/avatar"
+)
+```
+
+
+## License
+
+The package may be used under the terms of the BSD 2-Clause License a copy of
+which may be found in the [`LICENSE`] file.
+
+Unless you explicitly state otherwise, any contribution submitted for inclusion
+in the work by you shall be licensed as above, without any additional terms or
+conditions.
+
+[`LICENSE`]: ./LICENSE
A avatars.go => avatars.go +60 -0
@@ 0,0 1,60 @@
+// Package avatar contains functions for creating user avatars.
+package avatar // import "code.soquee.net/avatar"
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+ "image/png"
+ "net/http"
+
+ colorHash "mellium.im/xmpp/color"
+)
+
+const (
+ edgeLen = 512
+)
+
+type circle struct {
+ p image.Point
+ r int
+}
+
+func (c *circle) ColorModel() color.Model {
+ return color.AlphaModel
+}
+
+func (c *circle) Bounds() image.Rectangle {
+ return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
+}
+
+func (c *circle) At(x, y int) color.Color {
+ xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
+ if xx*xx+yy*yy < rr*rr {
+ return color.Alpha{255}
+ }
+ return color.Alpha{0}
+}
+
+// Handler returns an HTTP handler that renders a PNG avatar based on a hash of
+// the request path.
+func Handler() http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ c := colorHash.String(r.URL.Path, 187, colorHash.None)
+
+ img := image.NewPaletted(
+ image.Rect(0, 0, edgeLen, edgeLen),
+ color.Palette{c, color.White},
+ )
+ src := image.NewPaletted(
+ image.Rect(0, 0, edgeLen, edgeLen),
+ color.Palette{color.White},
+ )
+
+ draw.DrawMask(img, img.Bounds(), src, image.ZP, &circle{image.Point{X: edgeLen / 2, Y: edgeLen / 2}, edgeLen * 0.25}, image.ZP, draw.Over)
+
+ w.Header().Set("Content-Type", "image/png")
+ /* #nosec */
+ png.Encode(w, img)
+ }
+}
A avatars_test.go => avatars_test.go +22 -0
@@ 0,0 1,22 @@
+package avatar_test
+
+import (
+ "net/http/httptest"
+ "testing"
+
+ "code.soquee.net/avatar"
+)
+
+func TestAvatarContentType(t *testing.T) {
+ h := avatar.Handler()
+
+ req := httptest.NewRequest("GET", "/avatars", nil)
+ w := httptest.NewRecorder()
+ h.ServeHTTP(w, req)
+
+ const expect = "image/png"
+ ct := w.Header().Get("Content-Type")
+ if ct != expect {
+ t.Errorf("Unexpected Content-Type: want=%q, got=%q", expect, ct)
+ }
+}
A go.mod => go.mod +5 -0
@@ 0,0 1,5 @@
+module code.soquee.net/avatar
+
+go 1.11
+
+require mellium.im/xmpp v0.9.0
A go.sum => go.sum +9 -0
@@ 0,0 1,9 @@
+golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/image v0.0.0-20171214225156-12117c17ca67/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
+golang.org/x/net v0.0.0-20180216171745-136a25c244d3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+mellium.im/reader v0.0.4/go.mod h1:rtqlYmkQriWPf1vKtmH0iIellTSRU/1JUFV8MVS6vek=
+mellium.im/sasl v0.1.1/go.mod h1:vmQYTr1V4pFjgdorcqVEwA4WXck2n1JLx30pawPsSuY=
+mellium.im/xmlstream v0.13.1/go.mod h1:PDwtcQeiAyF7FrUBRWnVO91nIskvhOFYzOeftngiM7Q=
+mellium.im/xmpp v0.9.0 h1:xbrIopplkizD+1cyihUc23L0hFk4sQhZhjetK8l1SDw=
+mellium.im/xmpp v0.9.0/go.mod h1:Vs4gsNv+vnF4lYpBD63cjfwLIOqGqmdMJpgMhNwkGro=