2026-06-03 20:10:43 +08:00
|
|
|
package vision
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
|
|
|
|
|
"apigo.cc/go/file"
|
|
|
|
|
"apigo.cc/go/jsmod"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
jsmod.Register("vision", map[string]any{
|
|
|
|
|
// 基础操作
|
2026-06-21 10:18:46 +08:00
|
|
|
"Load": jsLoad,
|
|
|
|
|
"New": jsNew,
|
|
|
|
|
"Save": jsSave,
|
2026-06-03 20:10:43 +08:00
|
|
|
|
|
|
|
|
// 生成类
|
2026-06-21 10:18:46 +08:00
|
|
|
"GenerateQRCode": jsGenerateQRCode,
|
|
|
|
|
"GenerateBarcode": jsGenerateBarcode,
|
|
|
|
|
"GenerateCaptcha": jsGenerateCaptcha,
|
2026-06-03 20:10:43 +08:00
|
|
|
|
|
|
|
|
// 预览类 (直接写文件)
|
2026-06-21 10:18:46 +08:00
|
|
|
"GenerateImagePreview": jsGenerateImagePreview,
|
|
|
|
|
"GenerateVideoPreview": jsGenerateVideoPreview,
|
2026-06-03 20:10:43 +08:00
|
|
|
|
|
|
|
|
// 转换类
|
2026-06-21 10:18:46 +08:00
|
|
|
"Convert": jsConvert,
|
|
|
|
|
"Optimize": jsOptimize,
|
2026-06-03 20:10:43 +08:00
|
|
|
|
|
|
|
|
// 辅助工具
|
2026-06-21 10:18:46 +08:00
|
|
|
"LoadFonts": jsLoadFonts,
|
|
|
|
|
"Recognize": jsRecognize,
|
2026-06-03 20:10:43 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 10:18:46 +08:00
|
|
|
func jsLoad(ctx context.Context, path string) (*jsCanvas, error) {
|
|
|
|
|
p, err := file.VerifyPathForSafeMode(ctx, path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
c, err := Load(p)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return &jsCanvas{ctx: ctx, c: c}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsNew(ctx context.Context, w, h int, bg *string) *jsCanvas {
|
|
|
|
|
if bg != nil {
|
|
|
|
|
return &jsCanvas{ctx: ctx, c: New(w, h, *bg)}
|
|
|
|
|
}
|
|
|
|
|
return &jsCanvas{ctx: ctx, c: New(w, h)}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsSave(ctx context.Context, j *jsCanvas, path string, quality *int) error {
|
|
|
|
|
p, err := file.VerifyPathForSafeMode(ctx, path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
if quality != nil {
|
|
|
|
|
if err := Save(j.c, p, *quality); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if err := Save(j.c, p); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsGenerateQRCode(ctx context.Context, content string, size int) (*jsCanvas, error) {
|
|
|
|
|
c, err := GenerateQRCode(content, size)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return &jsCanvas{ctx: ctx, c: c}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsGenerateBarcode(ctx context.Context, content string, w, h int) (*jsCanvas, error) {
|
|
|
|
|
c, err := GenerateBarcode(content, w, h)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return &jsCanvas{ctx: ctx, c: c}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsGenerateCaptcha(ctx context.Context, opt *CaptchaOption) *jsCanvas {
|
|
|
|
|
return &jsCanvas{ctx: ctx, c: GenerateCaptcha(opt)}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsGenerateImagePreview(ctx context.Context, src, out string, w, h int) error {
|
|
|
|
|
pSrc, err := file.VerifyPathForSafeMode(ctx, src)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
pOut, err := file.VerifyPathForSafeMode(ctx, out)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
if err := GenerateImagePreview(pSrc, pOut, w, h); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsGenerateVideoPreview(ctx context.Context, src, out string, w, h int, interval *int) error {
|
|
|
|
|
pSrc, err := file.VerifyPathForSafeMode(ctx, src)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
pOut, err := file.VerifyPathForSafeMode(ctx, out)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
if interval != nil {
|
|
|
|
|
if err := GenerateVideoPreview(pSrc, pOut, w, h, *interval); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if err := GenerateVideoPreview(pSrc, pOut, w, h); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsConvert(ctx context.Context, src, dst string, quality *int) error {
|
|
|
|
|
pSrc, err := file.VerifyPathForSafeMode(ctx, src)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
pDst, err := file.VerifyPathForSafeMode(ctx, dst)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
if quality != nil {
|
|
|
|
|
if err := Convert(pSrc, pDst, *quality); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if err := Convert(pSrc, pDst); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsOptimize(ctx context.Context, path string, maxWidth int, quality int) error {
|
|
|
|
|
p, err := file.VerifyPathForSafeMode(ctx, path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
if err := Optimize(p, maxWidth, quality); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsLoadFonts(paths ...string) {
|
|
|
|
|
LoadFonts(paths...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func jsRecognize(ctx context.Context, path string) (string, error) {
|
|
|
|
|
p, err := file.VerifyPathForSafeMode(ctx, path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
res, err := Recognize(p)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 20:10:43 +08:00
|
|
|
// jsCanvas 包装器,支持链式调用
|
|
|
|
|
type jsCanvas struct {
|
|
|
|
|
ctx context.Context
|
|
|
|
|
c *Canvas
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *jsCanvas) Width() int { return j.c.Width() }
|
|
|
|
|
func (j *jsCanvas) Height() int { return j.c.Height() }
|
|
|
|
|
|
2026-06-10 11:18:04 +08:00
|
|
|
func (j *jsCanvas) Save(path string, quality *int) error {
|
2026-06-03 20:10:43 +08:00
|
|
|
p, err := file.VerifyPathForSafeMode(j.ctx, path)
|
|
|
|
|
if err != nil {
|
2026-06-21 10:18:46 +08:00
|
|
|
return jsmod.MakeError(err)
|
2026-06-03 20:10:43 +08:00
|
|
|
}
|
2026-06-10 11:18:04 +08:00
|
|
|
if quality != nil {
|
2026-06-21 10:18:46 +08:00
|
|
|
if err := Save(j.c, p, *quality); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2026-06-10 11:18:04 +08:00
|
|
|
}
|
2026-06-21 10:18:46 +08:00
|
|
|
if err := Save(j.c, p); err != nil {
|
|
|
|
|
return jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2026-06-03 20:10:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 效果与绘图 (返回自身以支持链式)
|
2026-06-21 10:18:46 +08:00
|
|
|
func (j *jsCanvas) Resize(w, h int) *jsCanvas { j.c.Resize(w, h); return j }
|
|
|
|
|
func (j *jsCanvas) Blur(sigma float64) *jsCanvas { j.c.Blur(sigma); return j }
|
|
|
|
|
func (j *jsCanvas) Grayscale() *jsCanvas { j.c.Grayscale(); return j }
|
|
|
|
|
func (j *jsCanvas) Invert() *jsCanvas { j.c.Invert(); return j }
|
|
|
|
|
func (j *jsCanvas) Rotate(angle float64) *jsCanvas { j.c.Rotate(angle); return j }
|
|
|
|
|
func (j *jsCanvas) FlipH() *jsCanvas { j.c.FlipH(); return j }
|
|
|
|
|
func (j *jsCanvas) FlipV() *jsCanvas { j.c.FlipV(); return j }
|
|
|
|
|
func (j *jsCanvas) Sharpen(sigma float64) *jsCanvas { j.c.Sharpen(sigma); return j }
|
2026-06-03 20:10:43 +08:00
|
|
|
func (j *jsCanvas) AdjustBrightness(p float64) *jsCanvas { j.c.AdjustBrightness(p); return j }
|
|
|
|
|
|
2026-06-21 10:18:46 +08:00
|
|
|
func (j *jsCanvas) Rect(x, y, w, h float64, opt *DrawStyle) *jsCanvas {
|
|
|
|
|
j.c.Rect(x, y, w, h, opt)
|
|
|
|
|
return j
|
|
|
|
|
}
|
|
|
|
|
func (j *jsCanvas) Circle(x, y, r float64, opt *DrawStyle) *jsCanvas {
|
|
|
|
|
j.c.Circle(x, y, r, opt)
|
|
|
|
|
return j
|
|
|
|
|
}
|
2026-06-03 20:10:43 +08:00
|
|
|
func (j *jsCanvas) Line(x1, y1, x2, y2 float64, opt *DrawStyle) *jsCanvas {
|
|
|
|
|
j.c.Line(x1, y1, x2, y2, opt)
|
|
|
|
|
return j
|
|
|
|
|
}
|
|
|
|
|
func (j *jsCanvas) DrawText(x, y float64, text string, opt *TextOption) *jsCanvas {
|
|
|
|
|
j.c.DrawText(x, y, text, opt)
|
|
|
|
|
return j
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 识别类
|
2026-06-21 10:18:46 +08:00
|
|
|
func (j *jsCanvas) DecodeAll() (string, error) {
|
|
|
|
|
res, err := j.c.DecodeAll()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|
|
|
|
|
func (j *jsCanvas) Recognize() (string, error) {
|
|
|
|
|
res, err := j.c.Recognize()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|
|
|
|
|
func (j *jsCanvas) DecodeQRCode() (string, error) {
|
|
|
|
|
res, err := j.c.DecodeQRCode()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", jsmod.MakeError(err)
|
|
|
|
|
}
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|