You've already forked VptPassiveAdapter
feat(config): 更新配置文件并新增自定义时间类型
- 修改 config.yaml 中的 API 地址、存储配置及设备信息 - 新增 Viid 配置项支持新功能模块 - 在 go.mod 和 go.sum 中更新依赖包版本,引入新的第三方库 - 添加 model/custom_time.go 文件实现自定义时间类型的 JSON 序列化与反序列化 - 调整 DTO 结构体以适配新的配置项 - 升级 OTLP 导出器相关依赖并移除旧的标准输出导出器 - 引入 Gin 框架及相关中间件提升服务性能 - 更新 Protobuf 和 gRPC 相关依赖至最新版本 - 增加 zap 日志库和 lumberjack 日志轮转支持 - 添加 sonic、json-iterator 等高性能 JSON 处理库优化数据解析效率 - 引入 testify 断言库增强测试代码可读性 - 更新 sync 包版本提高并发安全性 - 添加 mock 工具支持单元测试模拟对象 - 引入 validator/v10 实现请求参数校验功能 - 更新 crypto、net、text 等标准库依赖确保安全性和兼容性 - 增加 mimetype 库用于文件类型识别 - 引入 quic-go 支持 HTTP/3 协议通信 - 添加 base64x
This commit is contained in:
96
util/image.go
Normal file
96
util/image.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
_ "image/png" // Register PNG decoder just in case
|
||||
)
|
||||
|
||||
// GenerateThumbnail creates a thumbnail by cropping the original image.
|
||||
// The thumbnail size is 1/2 of the original dimensions.
|
||||
// The crop is centered on the provided coordinates (centerX, centerY), constrained to image bounds.
|
||||
func GenerateThumbnail(srcData []byte, centerX, centerY int) ([]byte, error) {
|
||||
// 1. Decode image
|
||||
img, _, err := image.Decode(bytes.NewReader(srcData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode image failed: %v", err)
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
origW := bounds.Dx()
|
||||
origH := bounds.Dy()
|
||||
|
||||
// 2. Calculate Target Dimensions (1/2 of original)
|
||||
targetW := origW / 2
|
||||
targetH := origH / 2
|
||||
|
||||
if targetW == 0 || targetH == 0 {
|
||||
return nil, fmt.Errorf("image too small to generate half-size thumbnail")
|
||||
}
|
||||
|
||||
// 3. Calculate Crop Origin (Top-Left)
|
||||
// cropX = centerX - targetW / 2
|
||||
cropX := centerX - targetW/2
|
||||
cropY := centerY - targetH/2
|
||||
|
||||
// 4. Constrain to Bounds
|
||||
if cropX < 0 {
|
||||
cropX = 0
|
||||
}
|
||||
if cropY < 0 {
|
||||
cropY = 0
|
||||
}
|
||||
if cropX+targetW > origW {
|
||||
cropX = origW - targetW
|
||||
}
|
||||
if cropY+targetH > origH {
|
||||
cropY = origH - targetH
|
||||
}
|
||||
|
||||
// Double check after adjustment
|
||||
if cropX < 0 {
|
||||
cropX = 0
|
||||
}
|
||||
if cropY < 0 {
|
||||
cropY = 0
|
||||
}
|
||||
|
||||
// 5. Perform Crop
|
||||
cropRect := image.Rect(cropX, cropY, cropX+targetW, cropY+targetH)
|
||||
|
||||
var dst image.Image
|
||||
|
||||
// Try to use SubImage if supported for performance
|
||||
type SubImager interface {
|
||||
SubImage(r image.Rectangle) image.Image
|
||||
}
|
||||
|
||||
if sub, ok := img.(SubImager); ok {
|
||||
dst = sub.SubImage(cropRect)
|
||||
} else {
|
||||
// Fallback: create new image and draw
|
||||
rgba := image.NewRGBA(image.Rect(0, 0, targetW, targetH))
|
||||
draw.Draw(rgba, rgba.Bounds(), img, cropRect.Min, draw.Src)
|
||||
dst = rgba
|
||||
}
|
||||
|
||||
// 6. Encode to JPEG
|
||||
var buf bytes.Buffer
|
||||
err = jpeg.Encode(&buf, dst, &jpeg.Options{Quality: 85})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encode thumbnail failed: %v", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Helper to process using LeftTop and RightBottom coordinates
|
||||
func GenerateThumbnailFromCoords(srcData []byte, ltX, ltY, rbX, rbY int) ([]byte, error) {
|
||||
// Calculate center based on the provided coordinates
|
||||
centerX := (ltX + rbX) / 2
|
||||
centerY := (ltY + rbY) / 2
|
||||
return GenerateThumbnail(srcData, centerX, centerY)
|
||||
}
|
||||
Reference in New Issue
Block a user