Skip to content

Commit

Permalink
优化代码逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
bitqiu committed May 19, 2024
1 parent c9f5703 commit 5054696
Show file tree
Hide file tree
Showing 13 changed files with 514 additions and 198 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### URL

> GET http://localhost:8080/captcha?code={code}&width={width}&height={height}
> GET /captcha?code={code}&width={width}&height={height}
### 参数

Expand All @@ -14,13 +14,13 @@

示例请求:

> GET http://localhost:8080/captcha?code=abcdef&width=120&height=30
> GET /captcha?code=abcdef&width=120&height=30
## 二维码图片生成

### URL

> GET http://localhost:8080/qrcode?text={text}&size={size}&level={level}&color={color}
> GET /qrcode?text={text}&size={size}&level={level}&color={color}
### 参数

Expand All @@ -31,4 +31,4 @@

示例请求:

> GET http://localhost:8080/qrcode?text=helloworld&size=400&level=L&color=549ecc
> GET /qrcode?text=helloworld&size=400&level=L&color=549ecc
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/bitqiu/pix-gen
go 1.21

require (
github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
Expand All @@ -14,14 +15,14 @@ require (
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/cors v1.7.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand All @@ -31,10 +32,11 @@ require (
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/image v0.13.0 // indirect
golang.org/x/image v0.16.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
18 changes: 13 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
Expand Down Expand Up @@ -38,6 +39,10 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
Expand All @@ -51,6 +56,8 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
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/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -74,8 +81,8 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand All @@ -84,12 +91,13 @@ golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
64 changes: 11 additions & 53 deletions handler/captcha.go
Original file line number Diff line number Diff line change
@@ -1,70 +1,28 @@
package handler

import (
"bytes"
"fmt"
"github.com/bitqiu/pix-gen/captcha"
"github.com/bitqiu/pix-gen/fonts"
"github.com/bitqiu/pix-gen/pkg/captcha"
"github.com/gin-gonic/gin"
"image/png"
"net/http"
"strconv"
)

var cap *captcha.Captcha

var defaultWidth = 120
var defaultHeight = 30

// HandleCaptcha 处理验证码生成请求的处理程序
func HandleCaptcha(c *gin.Context) {
cap = captcha.New()
cap.SetDisturbance(captcha.NORMAL)
//cap.SetFrontColor(color.RGBA{255, 0, 0, 255}, color.RGBA{0, 0, 255, 255}, color.RGBA{0, 153, 0, 255})
fontBytes, err := fonts.FontsFS.ReadFile("MiSans-Normal.ttf")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid"})
return
}
err = cap.AddFontFromBytes(fontBytes)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid font"})
return
}

// 解析width和height参数,如果不存在则使用默认值
widthStr := c.DefaultQuery("width", fmt.Sprintf("%d", defaultWidth))
heightStr := c.DefaultQuery("height", fmt.Sprintf("%d", defaultHeight))

// 转换字符串为int,并增加错误处理
width, err := strconv.ParseInt(widthStr, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid width format"})
return
}
height, err := strconv.ParseInt(heightStr, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid height format"})
return
}

// 检查code参数,如果存在则尝试解码验证码
// 获取并解析 width 和 height 参数,如果不存在则使用默认值
widthStr := c.DefaultQuery("width", fmt.Sprintf("%d", 120))
heightStr := c.DefaultQuery("height", fmt.Sprintf("%d", 30))
code := c.Query("code")

// 检查width和height的边界条件
if width <= 0 || height <= 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Width and height must be positive integers"})
// 调用 captcha 包生成验证码
captchaImage, err := captcha.GenerateCaptcha(widthStr, heightStr, code)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
cap.SetSize(int(width), int(height))

// 生成新的验证码
img := cap.CreateCustom(code)
// 返回验证码图像
c.Data(http.StatusOK, "image/png", captchaImage)

// 直接输出二进制图像数据
buffer := new(bytes.Buffer)
if err := png.Encode(buffer, img); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to encode image"})
return
}
c.Data(http.StatusOK, "image/png", buffer.Bytes())
}
85 changes: 13 additions & 72 deletions handler/qrcode.go
Original file line number Diff line number Diff line change
@@ -1,85 +1,26 @@
package handler

import (
"fmt"
qc "github.com/bitqiu/pix-gen/pkg/qrcode"
"github.com/gin-gonic/gin"
"github.com/skip2/go-qrcode"
"image/color"
"net/http"
"strconv"
"strings"
)

// HandleQrcode 是处理生成二维码请求的处理程序
func HandleQrcode(c *gin.Context) {
text := c.DefaultQuery("text", "null")
level := c.DefaultQuery("level", "H")
sizeQuery := c.DefaultQuery("size", fmt.Sprintf("%d", 300))
colorQuery := c.DefaultQuery("color", "000000")

// 转换字符串为int,并增加错误处理
size, err := strconv.ParseInt(sizeQuery, 10, 64)
text := c.DefaultQuery("text", "null") // 获取二维码的内容,默认为 "null"
level := c.DefaultQuery("level", "H") // 获取错误校验级别,默认为 "H"
sizeQuery := c.DefaultQuery("size", "300") // 获取二维码大小,默认为 300
colorQuery := c.DefaultQuery("color", "000000") // 获取前景颜色,默认为黑色
marginQuery := c.DefaultQuery("margin", "0") // 获取边距大小,默认为 0

// 调用 qc 包生成二维码
qrCode, err := qc.GenerateQRCode(text, level, sizeQuery, colorQuery, marginQuery)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid size format"})
return
}

// 设置错误校验级别
qrLevel := qrcode.Medium
switch level {
case "L":
qrLevel = qrcode.Low
case "M":
qrLevel = qrcode.Medium
case "Q":
qrLevel = qrcode.High
case "H":
qrLevel = qrcode.Highest
default:
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid QR code level"})
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

qrc, err := qrcode.New(text, qrLevel)
if err != nil {
c.JSON(http.StatusBadRequest, "Failed to create QR code")
return
}
qrc.DisableBorder = true

rgbaColor, err := hexToRGBA(colorQuery)
if err != nil {
c.JSON(http.StatusBadRequest, "Invalid color format")
return
}
qrc.ForegroundColor = rgbaColor

png, errEncode := qrc.PNG(int(size))
if errEncode != nil {
c.JSON(http.StatusBadRequest, "Failed to encode QR code image")
return
}

c.Data(http.StatusOK, "image/png", png)

}

// hexToRGBA 将16进制颜色转换为RGBA。
func hexToRGBA(hex string) (color.RGBA, error) {
hex = strings.TrimPrefix(strings.ToLower(hex), "#")
if len(hex) != 6 {
return color.RGBA{}, fmt.Errorf("invalid hex color format")
}
r, err := strconv.ParseUint(hex[:2], 16, 8)
if err != nil {
return color.RGBA{}, err
}
g, err := strconv.ParseUint(hex[2:4], 16, 8)
if err != nil {
return color.RGBA{}, err
}
b, err := strconv.ParseUint(hex[4:], 16, 8)
if err != nil {
return color.RGBA{}, err
}
return color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255}, nil
// 返回二维码图像
c.Data(http.StatusOK, "image/png", qrCode)
}
1 change: 0 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

func main() {
r := gin.Default()
// cors
config := cors.DefaultConfig()
config.AllowCredentials = true
config.AllowOrigins = []string{"*"}
Expand Down
21 changes: 12 additions & 9 deletions captcha/bilinear.go → pkg/captcha/bilinear.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import (
"math"
)

// 声明双线性插值的全局变量
var bili = Bilinear{}

// 定义 Bilinear 结构体
type Bilinear struct{}

// 双线性插值方法,根据给定的坐标获取插值后的颜色值
func (Bilinear) RGBA(src *image.RGBA, x, y float64) color.RGBA {
p := findLinearSrc(src.Bounds(), x, y)

// Array offsets for the surrounding pixels.
// 获取周围像素的偏移量
off00 := offRGBA(src, p.low.X, p.low.Y)
off01 := offRGBA(src, p.high.X, p.low.Y)
off10 := offRGBA(src, p.low.X, p.high.Y)
Expand Down Expand Up @@ -49,14 +52,15 @@ func (Bilinear) RGBA(src *image.RGBA, x, y float64) color.RGBA {
return c
}

// BilinearSrc 结构体,用于存储插值所需的参数
type BilinearSrc struct {
// Top-left and bottom-right interpolation sources
// 左上角和右下角的插值源
low, high image.Point
// Fraction of each pixel to take. The 0 suffix indicates
// top/left, and the 1 suffix indicates bottom/right.
// 每个像素的权重,0 后缀表示上/左,1 后缀表示下/右
frac00, frac01, frac10, frac11 float64
}

// findLinearSrc 查找双线性插值的源像素
func findLinearSrc(b image.Rectangle, sx, sy float64) BilinearSrc {
maxX := float64(b.Max.X)
maxY := float64(b.Max.Y)
Expand All @@ -80,10 +84,9 @@ func findLinearSrc(b image.Rectangle, sx, sy float64) BilinearSrc {
highY = maxY - 1
}

// In the variables below, the 0 suffix indicates top/left, and the
// 1 suffix indicates bottom/right.
// 下述变量中的 0 后缀表示上/左,1 后缀表示下/右

// Center of each surrounding pixel.
// 每个周围像素的中心
x00 := lowX + 0.5
y00 := lowY + 0.5
x01 := highX + 0.5
Expand All @@ -98,8 +101,7 @@ func findLinearSrc(b image.Rectangle, sx, sy float64) BilinearSrc {
high: image.Pt(int(highX), int(highY)),
}

// Literally, edge cases. If we are close enough to the edge of
// the image, curtail the interpolation sources.
// 边缘情况处理。如果我们足够接近图像的边缘,限制插值源
if lowX == highX && lowY == highY {
p.frac00 = 1.0
} else if sy-minY <= 0.5 && sx-minX <= 0.5 {
Expand Down Expand Up @@ -128,6 +130,7 @@ func findLinearSrc(b image.Rectangle, sx, sy float64) BilinearSrc {
return p
}

// offRGBA 获取给定坐标的像素偏移量
func offRGBA(src *image.RGBA, x, y int) int {
return (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4
}
Loading

0 comments on commit 5054696

Please sign in to comment.