From f5e32fac77c623aee750cc1fde4c41c4f2155284 Mon Sep 17 00:00:00 2001
From: Weilin Shi <934587911@qq.com>
Date: Tue, 19 Sep 2017 13:12:03 +0800
Subject: [PATCH] add: hsv for better random color update: readme
---
.travis.yml | 4 ++++
README.md | 37 +++++++++++++++++++++++++++++++++++++
captcha.go | 18 ++++++++++--------
example/captcha.png | Bin 0 -> 5565 bytes
fonts/gen.go | 6 +++---
hsva.go | 43 +++++++++++++++++++++++++++++++++++++++++++
hsva_test.go | 10 ++++++++++
7 files changed, 107 insertions(+), 11 deletions(-)
create mode 100644 .travis.yml
create mode 100644 README.md
create mode 100644 example/captcha.png
create mode 100644 hsva.go
create mode 100644 hsva_test.go
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..a9e5d5f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: go
+
+go:
+ - 1.9
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9fbeb54
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+> Package captcha provides a simple API for captcha generation
+
+
+
+[![GoDoc](https://godoc.org/github.com/steambap/captcha?status.svg)](https://godoc.org/github.com/steambap/captcha)
+[![Build Status](https://travis-ci.org/steambap/captcha.svg)](https://travis-ci.org/steambap/captcha)
+
+
+
+## install
+```
+go get github.com/steambap/captcha
+```
+
+## usage
+```Go
+func handle(w http.ResponseWriter, r *http.Request) {
+ // create a captcha of 150x50px
+ data, _ := captcha.New(150, 50)
+
+ // session come from other library such as gorilla/sessions
+ session.Values["captcha"] = data.Text
+ session.Save(r, w)
+ // send image data to client
+ data.WriteTo(w)
+}
+
+```
+
+[documentation](https://godoc.org/github.com/steambap/captcha) |
+[example](example/main.go)
+
+## sample image
+![image](example/captcha.png)
+
+## License
+[MIT](LICENSE.md)
diff --git a/captcha.go b/captcha.go
index 069be28..1716fd2 100644
--- a/captcha.go
+++ b/captcha.go
@@ -36,8 +36,8 @@ type Options struct {
// It defaults to 2.
CurveNumber int
- width int
- height int
+ width int
+ height int
}
func newDefaultOption(width, height int) *Options {
@@ -149,7 +149,7 @@ func drawSineCurve(img *image.NRGBA, opts *Options) {
if flip {
yFlip = -1.0
}
- curveColor := randomDarkGray()
+ curveColor := randomDarkColor()
for x1 := xStart; x1 <= xEnd; x1++ {
y := math.Sin(math.Pi*angle*float64(x1)/float64(opts.width)) * curveHeight * yFlip
@@ -157,10 +157,12 @@ func drawSineCurve(img *image.NRGBA, opts *Options) {
}
}
-func randomDarkGray() color.Gray {
- gray := rng.Intn(128) + 20
+func randomDarkColor() hsva {
+ hue := float64(rng.Intn(361)) / 360
+ saturation := 0.6 + rng.Float64() * 0.2
+ value := 0.25 + rng.Float64() * 0.2
- return color.Gray{Y: uint8(gray)}
+ return hsva{h: hue, s:saturation, v:value, a:uint8(255)}
}
func drawText(text string, img *image.NRGBA, opts *Options) error {
@@ -174,10 +176,10 @@ func drawText(text string, img *image.NRGBA, opts *Options) error {
fontSpacing := opts.width / len(text)
for idx, char := range text {
- fontScale := 1 + rng.Float64() * 0.5
+ fontScale := 1 + rng.Float64()*0.5
fontSize := float64(opts.height) / fontScale
ctx.SetFontSize(fontSize)
- ctx.SetSrc(image.NewUniform(randomDarkGray()))
+ ctx.SetSrc(image.NewUniform(randomDarkColor()))
x := fontSpacing*idx + fontSpacing/int(fontSize)
y := opts.height/6 + rng.Intn(opts.height/3) + int(fontSize/2)
pt := freetype.Pt(x, y)
diff --git a/example/captcha.png b/example/captcha.png
new file mode 100644
index 0000000000000000000000000000000000000000..abdd5a5a4b19ff9996b4c1ae0f9569933771dfc7
GIT binary patch
literal 5565
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
LNkvXXu0mjfW-iOm
literal 0
HcmV?d00001
diff --git a/fonts/gen.go b/fonts/gen.go
index e50392f..fbdb431 100644
--- a/fonts/gen.go
+++ b/fonts/gen.go
@@ -1,11 +1,11 @@
package main
import (
- "io/ioutil"
- "log"
"bytes"
"fmt"
"go/format"
+ "io/ioutil"
+ "log"
"path/filepath"
)
@@ -37,4 +37,4 @@ func main() {
if err := ioutil.WriteFile(filepath.Join("../", "font.go"), dst, 0666); err != nil {
log.Fatal(err)
}
-}
\ No newline at end of file
+}
diff --git a/hsva.go b/hsva.go
new file mode 100644
index 0000000..3035419
--- /dev/null
+++ b/hsva.go
@@ -0,0 +1,43 @@
+package captcha
+
+import "math"
+
+type hsva struct {
+ h, s, v float64
+ a uint8
+}
+
+// https://gist.github.com/mjackson/5311256
+func (c hsva) RGBA() (r, g, b, a uint32) {
+ var i = math.Floor(c.h * 6)
+ var f = c.h*6 - i
+ var p = c.v * (1.0 - c.s)
+ var q = c.v * (1.0 - f*c.s)
+ var t = c.v * (1 - (1-f)*c.s)
+
+ var red, green, blue float64
+ switch int(i) % 6 {
+ case 0:
+ red, green, blue = c.v, t, p
+ case 1:
+ red, green, blue = q, c.v, p
+ case 2:
+ red, green, blue = p, c.v, t
+ case 3:
+ red, green, blue = p, q, c.v
+ case 4:
+ red, green, blue = t, p, c.v
+ case 5:
+ red, green, blue = c.v, p, q
+ }
+
+ r = uint32(red * 255)
+ r |= r << 8
+ g = uint32(green * 255)
+ g |= g << 8
+ b = uint32(blue * 255)
+ b |= b << 8
+ a = uint32(c.a)
+ a |= a << 8
+ return
+}
diff --git a/hsva_test.go b/hsva_test.go
new file mode 100644
index 0000000..da6b69e
--- /dev/null
+++ b/hsva_test.go
@@ -0,0 +1,10 @@
+package captcha
+
+import (
+ "image/color"
+ "testing"
+)
+
+func TestHSVAInterface(t *testing.T) {
+ var _ color.Color = hsva{}
+}