109 lines
4.5 KiB
Go
109 lines
4.5 KiB
Go
package models
|
||
|
||
import (
|
||
"bytes"
|
||
"database/sql/driver"
|
||
"encoding/json"
|
||
"fmt"
|
||
"image"
|
||
"io/ioutil"
|
||
"main/configs"
|
||
"time"
|
||
|
||
"github.com/chai2010/webp"
|
||
"github.com/disintegration/imaging"
|
||
giftowebp "github.com/sizeofint/gif-to-webp"
|
||
)
|
||
|
||
type Image struct {
|
||
ID int `json:"id" gorm:"primary_key"` // ID
|
||
Name string `json:"name"` // 名称
|
||
Hash string `json:"hash"` // 哈希值
|
||
Path string `json:"path"` // 路径
|
||
Type string `json:"type"` // 类型
|
||
Size int `json:"size"` // 大小
|
||
Width int `json:"width"` // 宽度
|
||
Height int `json:"height"` // 高度
|
||
Format string `json:"format"` // 格式
|
||
Prompt string `json:"prompt"` // 提示词
|
||
NegativePrompt string `json:"negative_prompt"` // 负向提示
|
||
Steps int `json:"steps"` // 迭代步数 (Steps 1~150)
|
||
CfgScale int `json:"cfg_scale"` // 引导比例(minimum: 1; maximum: 20)
|
||
SamplerName string `json:"sampler_name"` // 采样器名称
|
||
Seed int `json:"seed"` // 随机种子(minimum: 0; maximum: 2147483647)
|
||
FromImage int `json:"from_image"` // 来源图片(如果是从图片生成的, 则记录来源图片的ID)
|
||
Task string `json:"task"` // 任务编号(uuid)
|
||
Status string `json:"status"` // 任务状态(queued|running|finished|failed)
|
||
Progress int `json:"progress"` // 任务进度(0-100)
|
||
Public bool `json:"public"` // 是否公开
|
||
UserID int `json:"user_id"` // 用户ID
|
||
ModelID int `json:"model_id"` // 模型ID
|
||
Preview string `json:"preview" gorm:"-"` // 实时预览 base64
|
||
User User `json:"user" gorm:"-"` // 用户
|
||
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
|
||
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
|
||
}
|
||
|
||
// 将图片输出为指定尺寸的 webp 格式(默认使用 Lanczos 缩放算法)
|
||
func (img *Image) ToWebP(width int, height int, fit string) ([]byte, error) {
|
||
// 從絕對路徑讀原始圖片
|
||
data, err := ioutil.ReadFile(fmt.Sprintf("data/images/%d", img.ID))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
// 將原始圖片轉換為 Image
|
||
imageData, format, err := image.Decode(bytes.NewReader(data))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
// 如果原图是GIF格式的动态图片,直接不作尺寸处理,直接转换为webp格式
|
||
if format == "gif" {
|
||
converter := giftowebp.NewConverter()
|
||
converter.LoopCompatibility = true // 是否兼容循环动画
|
||
converter.WebPConfig.SetLossless(1) // 0 有损压缩 1无损压缩
|
||
converter.WebPConfig.SetMethod(6) // 压缩速度 0-6 0最快 6质量最好
|
||
converter.WebPConfig.SetQuality(100) // 压缩质量 0-100
|
||
converter.WebPAnimEncoderOptions.SetKmin(9)
|
||
converter.WebPAnimEncoderOptions.SetKmax(17)
|
||
return converter.Convert(data)
|
||
}
|
||
// 如果指定了宽高却没有指定fit, 则默认fit为cover
|
||
if width != 0 && height != 0 && fit == "" {
|
||
fit = "cover"
|
||
}
|
||
// 如果未指定宽高和fit, 则不缩放图片直接返回webp
|
||
if width == 0 && height == 0 && fit == "" {
|
||
return webp.EncodeRGBA(imageData, 100)
|
||
}
|
||
switch fit {
|
||
case "cover":
|
||
return webp.EncodeRGBA(imaging.Fill(imageData, width, height, imaging.Center, imaging.Lanczos), 100)
|
||
case "contain":
|
||
return webp.EncodeRGBA(imaging.Fit(imageData, width, height, imaging.Lanczos), 100)
|
||
case "fill":
|
||
return webp.EncodeRGBA(imaging.Fill(imageData, width, height, imaging.Center, imaging.Lanczos), 100)
|
||
case "inside":
|
||
return webp.EncodeRGBA(imaging.Fit(imageData, width, height, imaging.Lanczos), 100)
|
||
case "outside":
|
||
return webp.EncodeRGBA(imaging.Fill(imageData, width, height, imaging.Center, imaging.Lanczos), 100)
|
||
case "scale-down":
|
||
return webp.EncodeRGBA(imaging.Fit(imageData, width, height, imaging.Lanczos), 100)
|
||
default:
|
||
return webp.EncodeRGBA(imaging.Resize(imageData, width, height, imaging.Lanczos), 100)
|
||
}
|
||
}
|
||
|
||
func init() {
|
||
configs.ORMDB().AutoMigrate(&Image{})
|
||
}
|
||
|
||
type ImageList []string
|
||
|
||
func (list *ImageList) Scan(value interface{}) error {
|
||
return json.Unmarshal(value.([]byte), list)
|
||
}
|
||
|
||
func (list ImageList) Value() (driver.Value, error) {
|
||
return json.Marshal(list)
|
||
}
|