From a0e2a2b47b8bb0eba2dcc7f093fef5374671b1f4 Mon Sep 17 00:00:00 2001 From: AI Engineer Date: Wed, 3 Jun 2026 20:10:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E9=BD=90=20Tag=20v1.5.0=20=EF=BC=88By?= =?UTF-8?q?=20AI=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 11 ++-- go.sum | 22 ++++---- js_export.go | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 js_export.go diff --git a/go.mod b/go.mod index 5b2c155..75f4f44 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,10 @@ module apigo.cc/go/vision go 1.25.0 require ( - apigo.cc/go/cast v1.3.3 - apigo.cc/go/file v1.3.2 - apigo.cc/go/rand v1.3.1 + apigo.cc/go/cast v1.5.0 + apigo.cc/go/file v1.5.0 + apigo.cc/go/jsmod v1.5.0 + apigo.cc/go/rand v1.5.0 github.com/boombuler/barcode v1.1.0 github.com/disintegration/imaging v1.6.2 github.com/flopp/go-findfont v0.1.0 @@ -16,8 +17,8 @@ require ( ) require ( - apigo.cc/go/encoding v1.3.1 // indirect - apigo.cc/go/safe v1.3.1 // indirect + apigo.cc/go/encoding v1.5.0 // indirect + apigo.cc/go/safe v1.5.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/kr/text v0.2.0 // indirect golang.org/x/crypto v0.51.0 // indirect diff --git a/go.sum b/go.sum index 5985ca0..23e7503 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,15 @@ -apigo.cc/go/cast v1.3.3 h1:aln5eDR5DZVWVzZ/y5SJh1gQNgWv2sT82I25NaO9g34= -apigo.cc/go/cast v1.3.3/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk= -apigo.cc/go/encoding v1.3.1 h1:y8O58KYAyulkThg1O2ji2BqjnFoSvk42sit9I3z+K7Y= -apigo.cc/go/encoding v1.3.1/go.mod h1:xAJk5b83VZ31mXMTnyp0dfMoBKfT/AHDn0u+cQfojgY= -apigo.cc/go/file v1.3.2 h1:pu4oiDyiqgj3/eykfnJf+/6+A9v/Z0b3ClP5XK+lwG4= -apigo.cc/go/file v1.3.2/go.mod h1:vci4h0Pz94mV6dkniQkuyBYERVYeq7/LX4jJVuCg9hs= -apigo.cc/go/rand v1.3.1 h1:7FvsI6PtQ5XrWER0dTiLVo0p7GIxRidT/TBKhVy93j8= -apigo.cc/go/rand v1.3.1/go.mod h1:mZ/4Soa3bk+XvDaqPWJuUe1bfEi4eThBj1XmEAuYxsk= -apigo.cc/go/safe v1.3.1 h1:irTCqPAC97gGsX/Lw5AzLelDt1xXLEZIAaVhLELWe9Q= -apigo.cc/go/safe v1.3.1/go.mod h1:XdOpBhN2vkImalaykYXXmEpczqWa1y3ah6/Q72cdRqE= +apigo.cc/go/cast v1.5.0 h1:UBGJtFQ8eJPMQXs37cUgqd7YQo1zI9opuSDBDmn2/pE= +apigo.cc/go/cast v1.5.0/go.mod h1:z2GW5p5WCZGEqVVIJUdhl232vRbLf2Qu4EDlEakX/D8= +apigo.cc/go/encoding v1.5.0 h1:EJNdRVDOMoI2DAvZwQNQTbYuqB/6zsEzvg7lS5pQI+I= +apigo.cc/go/encoding v1.5.0/go.mod h1:8++NfZj3hWig0qh2g7GQRw/4LpSvCYMWUZ+8J+x58cA= +apigo.cc/go/file v1.5.0 h1:Fh1NSDBqaxjuXYJ71yPHPXVJ8BFEv/AGS3l+jkLi5uw= +apigo.cc/go/file v1.5.0/go.mod h1:4YhOGgBINTpmmmgws3H8LAyXQQBGzBp44hYUoCS+kr0= +apigo.cc/go/jsmod v1.5.0 h1:JgQtJNiJWy1NOP9AzE8NX5VXJkpO/x3GqLsCCSny5Ec= +apigo.cc/go/jsmod v1.5.0/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw= +apigo.cc/go/rand v1.5.0 h1:1o8hh8fhdBuk1/h02IvugvamuT3dkWbVJrqEJVQKB2E= +apigo.cc/go/rand v1.5.0/go.mod h1:Lh98S2dm9UY0X+M+kNQQEKyXHG5pcCKSFPyXN0QCGdk= +apigo.cc/go/safe v1.5.0 h1:W1NblmcU8cex1f9Y5z8mNLUJOzZTE1s6fszb3FbhGnk= +apigo.cc/go/safe v1.5.0/go.mod h1:OfQ5d6COePSGEuPvMeOk6KagX2sezw7nvKh7exj9SeM= github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo= github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= diff --git a/js_export.go b/js_export.go new file mode 100644 index 0000000..82bc7ef --- /dev/null +++ b/js_export.go @@ -0,0 +1,152 @@ +package vision + +import ( + "context" + + "apigo.cc/go/file" + "apigo.cc/go/jsmod" +) + +func init() { + jsmod.Register("vision", map[string]any{ + // 基础操作 + "load": func(ctx context.Context, path string) (*jsCanvas, error) { + p, err := file.VerifyPathForSafeMode(ctx, path) + if err != nil { + return nil, err + } + c, err := Load(p) + if err != nil { + return nil, err + } + return &jsCanvas{ctx: ctx, c: c}, nil + }, + "new": func(ctx context.Context, w, h int, bg ...string) *jsCanvas { + return &jsCanvas{ctx: ctx, c: New(w, h, bg...)} + }, + "save": func(ctx context.Context, j *jsCanvas, path string, quality ...int) error { + p, err := file.VerifyPathForSafeMode(ctx, path) + if err != nil { + return err + } + return Save(j.c, p, quality...) + }, + + // 生成类 + "generateQRCode": func(ctx context.Context, content string, size int) (*jsCanvas, error) { + c, err := GenerateQRCode(content, size) + if err != nil { + return nil, err + } + return &jsCanvas{ctx: ctx, c: c}, nil + }, + "generateBarcode": func(ctx context.Context, content string, w, h int) (*jsCanvas, error) { + c, err := GenerateBarcode(content, w, h) + if err != nil { + return nil, err + } + return &jsCanvas{ctx: ctx, c: c}, nil + }, + "generateCaptcha": func(ctx context.Context, opt *CaptchaOption) *jsCanvas { + return &jsCanvas{ctx: ctx, c: GenerateCaptcha(opt)} + }, + + // 预览类 (直接写文件) + "generateImagePreview": func(ctx context.Context, src, out string, w, h int) error { + pSrc, err := file.VerifyPathForSafeMode(ctx, src) + if err != nil { + return err + } + pOut, err := file.VerifyPathForSafeMode(ctx, out) + if err != nil { + return err + } + return GenerateImagePreview(pSrc, pOut, w, h) + }, + "generateVideoPreview": func(ctx context.Context, src, out string, w, h int, interval ...int) error { + pSrc, err := file.VerifyPathForSafeMode(ctx, src) + if err != nil { + return err + } + pOut, err := file.VerifyPathForSafeMode(ctx, out) + if err != nil { + return err + } + return GenerateVideoPreview(pSrc, pOut, w, h, interval...) + }, + + // 转换类 + "convert": func(ctx context.Context, src, dst string, quality ...int) error { + pSrc, err := file.VerifyPathForSafeMode(ctx, src) + if err != nil { + return err + } + pDst, err := file.VerifyPathForSafeMode(ctx, dst) + if err != nil { + return err + } + return Convert(pSrc, pDst, quality...) + }, + "optimize": func(ctx context.Context, path string, maxWidth int, quality int) error { + p, err := file.VerifyPathForSafeMode(ctx, path) + if err != nil { + return err + } + return Optimize(p, maxWidth, quality) + }, + + // 辅助工具 + "loadFonts": LoadFonts, + "recognize": func(ctx context.Context, path string) (string, error) { + p, err := file.VerifyPathForSafeMode(ctx, path) + if err != nil { + return "", err + } + return Recognize(p) + }, + }) +} + +// 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() } + +func (j *jsCanvas) Save(path string, quality ...int) error { + p, err := file.VerifyPathForSafeMode(j.ctx, path) + if err != nil { + return err + } + return Save(j.c, p, quality...) +} + +// 效果与绘图 (返回自身以支持链式) +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 } +func (j *jsCanvas) AdjustBrightness(p float64) *jsCanvas { j.c.AdjustBrightness(p); return j } + +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 } +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 +} + +// 识别类 +func (j *jsCanvas) DecodeAll() (string, error) { return j.c.DecodeAll() } +func (j *jsCanvas) Recognize() (string, error) { return j.c.Recognize() } +func (j *jsCanvas) DecodeQRCode() (string, error) { return j.c.DecodeQRCode() }