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"` // 负向提示 NumInferenceSteps int `json:"num_inference_steps"` // 推理步数(minimum: 1; maximum: 500) GuidanceScale float32 `json:"guidance_scale"` // 引导比例(minimum: 1; maximum: 20) Scheduler string `json:"scheduler"` // 调度器(DDIM|K_EULER|DPMSolverMultistep|K_EULER_ANCESTRAL|PNDM|KLMS) Seed string `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 CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"` } //// 参数校验 //func (img *Image) ParameterCheck() (err error) { // if img.NumInferenceSteps <= 0 { // img.NumInferenceSteps = 20 // } // if img.NumInferenceSteps > 500 { // img.NumInferenceSteps = 500 // } // if img.GuidanceScale <= 0 { // img.GuidanceScale = 1 // } // if img.GuidanceScale > 20 { // img.GuidanceScale = 20 // } // if img.Scheduler == "" { // img.Scheduler = "DDIM" // } // if img.Seed == "" { // img.Seed = "0" // } // return //} // 将图片输出为指定尺寸的 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) }