diff --git a/image/define.go b/image/define.go new file mode 100644 index 0000000..30025ef --- /dev/null +++ b/image/define.go @@ -0,0 +1,29 @@ +// Package image ... +// +// Description : image ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2021-11-29 2:27 下午 +package image + +const ( + // TypeGIF ... + TypeGIF = "gif" + // TypePNG ... + TypePNG = "png" + // TypeJPG ... + TypeJPG = "jpg" + // TypeJPEG ... + TypeJPEG = "jpeg" +) + +// ImgInfo ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2:45 下午 2021/11/29 +type ImgInfo struct { + Path string + Type string +} diff --git a/image/gif.go b/image/gif.go new file mode 100644 index 0000000..1f7f06b --- /dev/null +++ b/image/gif.go @@ -0,0 +1,195 @@ +// Package image ... +// +// Description : GIF相关操作 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2021-11-29 12:43 下午 +package image + +import ( + "errors" + "image" + "image/color" + "image/color/palette" + "image/draw" + "image/gif" + "image/jpeg" + "image/png" + "os" + + "git.zhangdeman.cn/zhangdeman/gopkg/util" +) + +var ( + // GIF ... + GIF *GIFConvert +) + +func init() { + GIF = &GIFConvert{} +} + +// GIFConvert gif转换 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 12:44 下午 2021/11/29 +type GIFConvert struct { +} + +// Generate 生成 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2:13 下午 2021/11/29 +func (g *GIFConvert) Generate(sourceImageList []string, savePath string) error { + if len(sourceImageList) == 0 { + return errors.New("source image list is empty") + } + var ( + err error + formatImgList []ImgInfo + disposals []byte + images []*image.Paletted + // 播放速度设置 , 100 次多少秒 + delays []int + ) + if formatImgList, err = g.checkImageType(sourceImageList); nil != err { + return err + } + + for _, imgInfo := range formatImgList { + img, gErr := g.loadImage(imgInfo) + if nil != gErr { + return gErr + } + cp := g.getPalette(img) + //cp:=append(palette.WebSafe,color.Transparent) + disposals = append(disposals, gif.DisposalBackground) //透明图片需要设置 + p := image.NewPaletted(image.Rect(0, 0, 640, 996), cp) + draw.Draw(p, p.Bounds(), img, image.ZP, draw.Src) + images = append(images, p) + delays = append(delays, 100) + } + gifInstance := &gif.GIF{ + Image: images, + Delay: delays, + LoopCount: -1, + Disposal: disposals, + } + f, fErr := os.Create(savePath) + if fErr != nil { + return fErr + } + defer func() { _ = f.Close() }() + return gif.EncodeAll(f, gifInstance) +} + +// checkImageType 检测图片类型 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2:16 下午 2021/11/29 +func (g *GIFConvert) checkImageType(sourceImageList []string) ([]ImgInfo, error) { + result := make([]ImgInfo, 0) + for _, item := range sourceImageList { + imgType := util.GetFileType(item) + if len(imgType) == 0 { + return result, errors.New(item + " parse img type fail!") + } + result = append(result, ImgInfo{ + Path: item, + Type: imgType, + }) + } + return result, nil +} + +// loadImage 加载图片内容 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2:52 下午 2021/11/29 +func (g *GIFConvert) loadImage(imgInfo ImgInfo) (image.Image, error) { + var ( + err error + f *os.File + ) + if f, err = os.Open(imgInfo.Path); nil != err { + return nil, err + } + + defer func() { + _ = f.Close() + }() + + switch imgInfo.Type { + case TypePNG: + if img, imgErr := png.Decode(f); nil != err { + return nil, imgErr + } else { + return img, nil + } + case TypeJPEG: + fallthrough + case TypeJPG: + if img, imgErr := jpeg.Decode(f); nil != err { + return nil, imgErr + } else { + return img, nil + } + case TypeGIF: + if img, imgErr := gif.Decode(f); nil != err { + return nil, imgErr + } else { + return img, nil + } + default: + return nil, errors.New(imgInfo.Type + " is not support!") + } +} + +// getPalette ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 3:01 下午 2021/11/29 +func (g *GIFConvert) getPalette(m image.Image) color.Palette { + p := color.Palette{color.RGBA{0x00, 0x00, 0x00, 0x00}} + p9 := color.Palette(palette.Plan9) + b := m.Bounds() + black := false + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + c := m.At(x, y) + cc := p9.Convert(c) + if cc == p9[0] { + black = true + } + if g.isInPalette(p, cc) == -1 { + p = append(p, cc) + } + } + } + if len(p) < 256 && black == true { + p[0] = color.RGBA{0x00, 0x00, 0x00, 0x00} // transparent + p = append(p, p9[0]) + } + return p +} + +// isInPalette ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 3:01 下午 2021/11/29 +func (g *GIFConvert) isInPalette(p color.Palette, c color.Color) int { + ret := -1 + for i, v := range p { + if v == c { + return i + } + } + return ret +} diff --git a/image/gif_test.go b/image/gif_test.go new file mode 100644 index 0000000..6ab97ed --- /dev/null +++ b/image/gif_test.go @@ -0,0 +1,24 @@ +// Package image ... +// +// Description : image ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2021-11-29 3:10 下午 +package image + +import ( + "fmt" + "testing" +) + +// TestGIFConvert_Generate ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 3:10 下午 2021/11/29 +func TestGIFConvert_Generate(t *testing.T) { + sourceImgList := []string{"./test/1.jpeg", "./test/2.jpeg", "./test/3.jpeg", "./test/4.jpeg"} + savePath := "./test/test.gif" + fmt.Println(GIF.Generate(sourceImgList, savePath)) +} diff --git a/image/test/1.jpeg b/image/test/1.jpeg new file mode 100644 index 0000000..22ec3c5 Binary files /dev/null and b/image/test/1.jpeg differ diff --git a/image/test/2.jpeg b/image/test/2.jpeg new file mode 100644 index 0000000..9cd1302 Binary files /dev/null and b/image/test/2.jpeg differ diff --git a/image/test/3.jpeg b/image/test/3.jpeg new file mode 100644 index 0000000..318038c Binary files /dev/null and b/image/test/3.jpeg differ diff --git a/image/test/4.jpeg b/image/test/4.jpeg new file mode 100644 index 0000000..9712bce Binary files /dev/null and b/image/test/4.jpeg differ diff --git a/image/test/test.gif b/image/test/test.gif new file mode 100644 index 0000000..b9aa434 Binary files /dev/null and b/image/test/test.gif differ diff --git a/util/file.go b/util/file.go index c7b00df..905f4d2 100644 --- a/util/file.go +++ b/util/file.go @@ -85,3 +85,16 @@ func IsFileExist(filePath string) (bool, bool) { f, err := os.Stat(filePath) return nil == err || os.IsExist(err), (nil == err || os.IsExist(err)) && !f.IsDir() } + +// GetFileType 获取文件类型 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2:30 下午 2021/11/29 +func GetFileType(fileName string) string { + fileArr := strings.Split(fileName, ".") + if len(fileArr) < 2 { + return "" + } + return strings.ToLower(fileArr[len(fileArr)-1]) +}