Browse Source

Update poster method

朱金辉 2 years ago
parent
commit
320dd36af2
12 changed files with 407 additions and 372 deletions
  1. 15 16
      controller/poster.go
  2. 0 1
      go.mod
  3. 1 1
      middleware/logger_mw.go
  4. 84 0
      util/image/avatar.go
  5. 0 132
      util/image/back.txt
  6. 71 0
      util/image/basic.go
  7. 25 21
      util/image/color.go
  8. 68 95
      util/image/image.go
  9. 60 14
      util/image/poster.go
  10. 35 33
      util/image/qr.go
  11. 46 55
      util/image/text.go
  12. 2 4
      util/jobs/test_cron.go

+ 15 - 16
controller/poster.go

@@ -4,7 +4,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"github.com/gin-gonic/gin"
-	"github.com/gographics/gmagick"
 	setting "icloudapp.cn/tools/config"
 	"icloudapp.cn/tools/entity"
 	"icloudapp.cn/tools/model"
@@ -22,19 +21,21 @@ func Preview(ctx *gin.Context) {
 	path, _ := os.Getwd()
 
 	thumb := image.NewImage(setting.Conf.CacheDir)
-	file, err := thumb.Thumb(fmt.Sprintf("%s/%s", path, file), image.NewSize(300, 300))
+	thumb.SetSize(300, 300)
+	thumb.SetFile(fmt.Sprintf("%s/%s", path, file))
+	fileWm, err := thumb.Thumb()
 
 	if err != nil {
 		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
 		return
 	}
-
-	stream, err := os.ReadFile(file)
+	defer fileWm.Destroy()
+	//不写入文件,直接返回给浏览器
+	_, err = ctx.Writer.Write(fileWm.GetImageBlob())
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
+		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
 		return
 	}
-	ctx.Writer.WriteString(string(stream))
 }
 
 func Image(ctx *gin.Context) {
@@ -98,24 +99,22 @@ func Cut(ctx *gin.Context) {
 
 	path, _ := os.Getwd()
 
-	gmagick.Initialize()
-	defer gmagick.Terminate()
-
 	thumb := image.NewImage(setting.Conf.CacheDir)
-	file, err := thumb.Cut(fmt.Sprintf("%s/%s", path, file), image.NewSize(400, 400), "center")
+	thumb.SetSize(400, 400)
+	thumb.SetFile(fmt.Sprintf("%s/%s", path, file))
+	fileWm, err := thumb.Cut("center")
 
 	if err != nil {
 		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
 		return
 	}
-
-	stream, err := os.ReadFile(file)
+	defer fileWm.Destroy()
+	//不写入文件,直接返回给浏览器
+	_, err = ctx.Writer.Write(fileWm.GetImageBlob())
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
+		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
 		return
 	}
-
-	ctx.Writer.WriteString(string(stream))
 }
 
 func SavePoster(ctx *gin.Context) {
@@ -181,7 +180,7 @@ func DrawImage(ctx *gin.Context) {
 	if posterJson.Quality == 0 {
 		posterJson.Quality = 50
 	}
-
+	posterJson.Quality = 30
 	if err = file.SetCompressionQuality(uint(posterJson.Quality)); err != nil {
 		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetCompressionQuality err: %s", err.Error()), []interface{}{})
 		return

+ 0 - 1
go.mod

@@ -6,7 +6,6 @@ require (
 	github.com/fsnotify/fsnotify v1.6.0
 	github.com/gin-gonic/gin v1.9.0
 	github.com/go-redis/redis v6.15.9+incompatible
-	github.com/gographics/gmagick v1.0.0
 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
 	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 	github.com/robfig/cron v1.2.0

+ 1 - 1
middleware/logger_mw.go

@@ -12,7 +12,7 @@ import (
 )
 
 var (
-	logFilePath = "./"
+	logFilePath = "./log/"
 	logFileName = "system.log"
 )
 

+ 84 - 0
util/image/avatar.go

@@ -1 +1,85 @@
 package image
+
+import (
+	"gopkg.in/gographics/imagick.v3/imagick"
+	"icloudapp.cn/tools/util"
+)
+
+type Avatar struct {
+	Src             string
+	Border          float64
+	BackgroundColor string
+	AvatarImagick   *imagick.MagickWand
+	ViewSize        *Size
+	ViewPoint       *Point
+	Angle           float64
+}
+
+func NewAvatar() *Avatar {
+	return &Avatar{}
+}
+
+func (a *Avatar) SetBorder(border float64) {
+	a.Border = border
+}
+
+func (a *Avatar) SetBackgroundColor(backgroundColor string) {
+	a.BackgroundColor = backgroundColor
+}
+
+func (a *Avatar) SetAvatar(src string) {
+	a.Src = src
+}
+func (a *Avatar) SetSize(width, height uint) {
+	a.ViewSize = NewSize(width, height)
+}
+func (a *Avatar) SetViewPoint(x, y float64) {
+	a.ViewPoint = NewPoint(x, y)
+}
+func (a *Avatar) SetAngle(angle float64) {
+	a.Angle = angle
+}
+func (a *Avatar) Create() (*imagick.MagickWand, error) {
+	border := imagick.NewDrawingWand()
+	border.SetStrokeOpacity(1)
+	border.SetStrokeColor(Pixel(a.BackgroundColor))
+	border.SetFillColor(Pixel(a.BackgroundColor))
+	border.SetStrokeWidth(a.Border)
+	border.SetFontSize(72)
+	border.Circle(float64(a.ViewSize.Width)/2, float64(a.ViewSize.Height)/2, float64(a.ViewSize.Width)/2, float64(a.ViewSize.Height))
+
+	borderImagick := imagick.NewMagickWand()
+	if err := borderImagick.NewImage(a.ViewSize.Width+uint(a.Border), a.ViewSize.Height+uint(a.Border), OpacityPixel()); err != nil {
+		return nil, util.NewError("borderImagick.NewImage err : ", err.Error())
+	}
+	if err := borderImagick.DrawImage(border); err != nil {
+		return nil, util.NewError("borderImagick.DrawImage err : ", err.Error())
+	}
+
+	avatarImagick := imagick.NewMagickWand()
+	if err := avatarImagick.ReadImage(a.Src); err != nil {
+		return nil, util.NewError("avatarImagick.ReadImage err : ", err.Error())
+	}
+	if err := avatarImagick.ResizeImage(a.ViewSize.Width-uint(a.Border), a.ViewSize.Height-uint(a.Border), imagick.FILTER_LANCZOS); err != nil {
+		return nil, util.NewError("avatarImagick.ResizeImage err : ", err.Error())
+	}
+	if err := avatarImagick.SetImageFormat("PNG"); err != nil {
+		return nil, util.NewError("avatarImagick.SetImageFormat err : ", err.Error())
+	}
+	if err := borderImagick.CompositeImage(avatarImagick,
+		imagick.COMPOSITE_OP_OVER,
+		true, int(a.Border), int(a.Border)); err != nil {
+		return nil, util.NewError("borderImagick.CompositeImage err : ", err.Error())
+	}
+
+	if a.Angle > 0 {
+		if err := borderImagick.RotateImage(OpacityPixel(), a.Angle); err != nil {
+			return nil, util.NewError("qrWM.RotateImage err : ", err.Error())
+		}
+		alignX := float64(borderImagick.GetImageWidth()-a.ViewSize.Width) / 2
+		alignY := float64(borderImagick.GetImageHeight()-a.ViewSize.Height) / 2
+		a.ViewPoint.X = alignX
+		a.ViewPoint.Y = alignY
+	}
+	return borderImagick, nil
+}

+ 0 - 132
util/image/back.txt

@@ -1,132 +0,0 @@
-
-
-func (p *Poster) Create2(json PosterJson) (string, error) {
-	cachePath := fmt.Sprintf("%s/cache", util.Getwd())
-	image := NewImage(util.Getwd())
-	image.SetCache(cachePath)
-	if json.Use == "bgUrl" {
-		json.Background = ""
-	}
-	if json.Use == "bgc" {
-		json.BackgroundUrl = ""
-	}
-	poster, err := image.Create(json.BackgroundUrl, json.Background, *NewSize(json.Width, json.Height))
-
-	if err != nil {
-		return "", err
-	}
-	defer poster.Destroy()
-
-	itemLen := len(json.Items)
-	destChan := make(chan *PosterMW, itemLen)
-
-	defer close(destChan)
-
-	for index, item := range json.Items {
-		go func(item Items, index int) {
-			var drawErr error
-			var drawMW *gmagick.MagickWand
-			var updated Items
-			if item.Type == "text" {
-				drawMW, updated, drawErr = p.Text(item)
-			}
-			if item.Type == "image" {
-				drawMW, drawErr = image.Resize(item.Value, item.Width, item.Height, item.Angle)
-			}
-			if item.Type == "qrcode" {
-				drawMW, updated, drawErr = p.QRCode(item)
-			}
-			destChan <- &PosterMW{Index: index, Image: drawMW, Item: updated, Err: drawErr}
-		}(item, index)
-	}
-
-	var response = make([]*PosterMW, itemLen)
-	for i := 0; i < len(json.Items); i++ {
-		res := <-destChan
-		response[res.Index] = res
-	}
-	for i := 0; i < len(response); i++ {
-		draw := response[i].Image
-		errDraw := response[i].Err
-		if errDraw != nil {
-			continue
-		}
-		if errComposite := poster.CompositeImage(draw, gmagick.COMPOSITE_OP_OVER, response[i].Item.X, response[i].Item.Y); errComposite != nil {
-			fmt.Println("poster.CompositeImage text err : ", errComposite.Error())
-			continue
-		}
-	}
-
-	destFile := image.CacheFile("preview-poster", poster.GetImageFormat())
-	err = poster.WriteImage(destFile)
-
-	if err != nil {
-		return "", err
-	}
-	return destFile, nil
-}
-
-func (p *Poster) Create1(json PosterJson) (string, error) {
-	cachePath := fmt.Sprintf("%s/cache", util.Getwd())
-	image := NewImage(util.Getwd())
-	image.SetCache(cachePath)
-	if json.Use == "bgUrl" {
-		json.Background = ""
-	}
-	if json.Use == "bgc" {
-		json.BackgroundUrl = ""
-	}
-	poster, err := image.Create(json.BackgroundUrl, json.Background, *NewSize(json.Width, json.Height))
-
-	if err != nil {
-		return "", err
-	}
-	defer poster.Destroy()
-
-	for _, item := range json.Items {
-
-		if item.Type == "text" {
-			drawText, _, errTxt := p.Text(item)
-			if errTxt != nil {
-				fmt.Println("drawText err: ", errTxt.Error())
-				continue
-			}
-			if errComposite := poster.CompositeImage(drawText, gmagick.COMPOSITE_OP_OVER, item.X, item.Y); errComposite != nil {
-				fmt.Println("poster.CompositeImage text err : ", errComposite.Error())
-				continue
-			}
-		}
-
-		if item.Type == "image" {
-			imageMW, errImage := image.Resize(item.Value, item.Width, item.Height, item.Angle)
-			if errImage != nil {
-				continue
-			}
-			if errComposite := poster.CompositeImage(imageMW, gmagick.COMPOSITE_OP_OVER, item.X, item.Y); errComposite != nil {
-				fmt.Println("poster.CompositeImage image err : ", errComposite.Error())
-				continue
-			}
-		}
-
-		if item.Type == "qrcode" {
-			qrWM, _, errQr := p.QRCode(item)
-			if errQr != nil {
-				fmt.Println("Qr err", errQr.Error())
-				continue
-			}
-			if errComposite := poster.CompositeImage(qrWM, gmagick.COMPOSITE_OP_OVER, item.X, item.Y); errComposite != nil {
-				fmt.Println("poster.CompositeImage qr err : ", errComposite.Error())
-				continue
-			}
-		}
-	}
-
-	//util.RandomStr(10);
-	destFile := image.CacheFile("preview-poster", poster.GetImageFormat())
-	err = poster.WriteImage(destFile)
-
-	if err != nil {
-		return "", err
-	}
-	return destFile, nil
-}

+ 71 - 0
util/image/basic.go

@@ -0,0 +1,71 @@
+package image
+
+type Basic struct {
+	Cache
+	Path       string
+	File       string
+	Background string
+	ViewPoint  *Point
+	ViewSize   *Size
+	Angle      float64
+}
+
+// SetAngle 设置旋转角度
+func (i *Basic) SetAngle(angle float64) {
+	i.Angle = angle
+}
+
+// SetSize 设置目标尺寸
+func (i *Basic) SetSize(width, height uint) {
+	i.ViewSize = NewSize(width, height)
+}
+
+// SetPoint 设置图片的x、y 坐标
+func (i *Basic) SetPoint(x, y float64) {
+	i.ViewPoint = NewPoint(x, y)
+}
+
+// SetFile 设置处理的图片
+func (i *Basic) SetFile(file string) {
+	i.File = file
+}
+
+// SetBackground 设置背景色
+func (i *Basic) SetBackground(background string) {
+	i.Background = background
+}
+
+// Position 计算裁切的x、y及宽高
+func (i *Basic) Position(position string, width, height uint, size *Size) (dx, dy int, dWidth, dHeight uint) {
+	x := 0
+	y := 0
+	scale := 1.0
+
+	if width < size.Width {
+		scale = CustomScale(width, size.Width)
+		width = size.Width
+	}
+	if height < size.Height {
+		scale = CustomScale(height, size.Height)
+		height = size.Height
+	}
+	paddingLeft := width - size.Width
+	paddingTop := height - size.Height
+
+	if position == "right" {
+		x = int(paddingLeft)
+	}
+	if position == "center" {
+		x = int(paddingLeft / 2)
+		y = int(paddingTop / 2)
+	}
+
+	if position == "right|center" {
+		x = int(paddingLeft)
+		y = int(paddingTop / 2)
+	}
+	if position == "left|center" {
+		y = int(paddingTop / 2)
+	}
+	return x, y, uint(float64(size.Width) * scale), uint(float64(size.Height) * scale)
+}

+ 25 - 21
util/image/color.go

@@ -34,7 +34,7 @@ func PaddingColorTo8(color string) string {
 			strBuild.WriteString(string(strRune[i]))
 			strBuild.WriteString(string(strRune[i]))
 		}
-		strBuild.WriteString("00")
+		strBuild.WriteString("FF")
 		hexColor = strBuild.String()
 	}
 	if len(hexColor) > 8 {
@@ -44,7 +44,7 @@ func PaddingColorTo8(color string) string {
 	if len(hexColor) < 8 {
 		padding := 8 - len(hexColor)
 		for i := 0; i < padding; i++ {
-			strBuild.WriteString("0")
+			strBuild.WriteString("F")
 		}
 	}
 	return strBuild.String()
@@ -57,44 +57,48 @@ func Alpha(color string) (string, Color, float64) {
 	hexColor := PaddingColorTo8(color)
 
 	color = util.CutString(hexColor, 0, 6)
-	alpha := util.CutString(hexColor, 6, 8)
-	hexAlpha, _ := strconv.ParseInt(alpha, 16, 10)
+
+	hexAlpha, _ := strconv.ParseInt(util.CutString(hexColor, 6, 8), 16, 10)
 	opacity = float64(int((float64(hexAlpha)/255)*100)) / 100
 	color = fmt.Sprintf("#%s", color)
 
 	var rgb Color
 	rgbRune := []rune(hexColor)
-	r, _ := strconv.ParseInt(string(rgbRune[0:2]), 16, 10)
-	rgb.Red = uint32(r)
 
-	R, _ := strconv.ParseUint(string(rgbRune[0:2]), 16, 8)
-	rgb.R = uint8(R)
+	red := string(rgbRune[0:2])
+	r, _ := strconv.ParseInt(red, 16, 10)
+	rgb.Red = uint32(r)
+	rgb.R = uint8(r)
 
-	g, _ := strconv.ParseInt(string(rgbRune[2:4]), 16, 10)
+	green := string(rgbRune[2:4])
+	g, _ := strconv.ParseInt(green, 16, 10)
 	rgb.Green = uint32(g)
+	rgb.G = uint8(g)
 
-	G, _ := strconv.ParseUint(string(rgbRune[2:4]), 16, 8)
-	rgb.G = uint8(G)
-
-	b, _ := strconv.ParseInt(string(rgbRune[4:6]), 16, 10)
+	blue := string(rgbRune[4:6])
+	b, _ := strconv.ParseInt(blue, 16, 10)
 	rgb.Blue = uint32(b)
+	rgb.B = uint8(b)
 
-	B, _ := strconv.ParseUint(string(rgbRune[4:6]), 16, 8)
-	rgb.G = uint8(B)
-
-	a, _ := strconv.ParseInt(string(rgbRune[6:8]), 16, 10)
+	alpha := string(rgbRune[6:8])
+	a, _ := strconv.ParseInt(alpha, 16, 10)
 	rgb.Alpha = uint32(a / 255)
-
-	A, _ := strconv.ParseUint(string(rgbRune[6:8]), 16, 8)
-	rgb.A = uint8(A)
+	rgb.A = uint8(a)
 
 	return color, rgb, opacity
 }
 
 // Pixel PixelWand
 func Pixel(color string) *imagick.PixelWand {
-	destColor, _, _ := Alpha(color)
+	destColor, _, alpha := Alpha(color)
 	g := imagick.NewPixelWand()
 	g.SetColor(destColor)
+	g.SetAlpha(alpha)
 	return g
 }
+
+func OpacityPixel() *imagick.PixelWand {
+	pw := imagick.NewPixelWand()
+	pw.SetColor("none")
+	return pw
+}

+ 68 - 95
util/image/image.go

@@ -8,169 +8,142 @@ import (
 )
 
 type Image struct {
-	Cache
+	Basic
 	Path string
 }
 
+// NewImage 创建新的Image对象
 func NewImage(path string) *Image {
-	return &Image{
+	ImageInstance := &Image{
 		Path: path,
 	}
+	//初始化指针相关数据,避免使用时nil了
+	ImageInstance.SetSize(0, 0)
+	ImageInstance.SetPoint(0, 0)
+	return ImageInstance
 }
 
-func (i *Image) Create(file, background string, size Size) (*imagick.MagickWand, error) {
-	if file != "" {
-		file = fmt.Sprintf("%s/%s", i.Path, file)
-		if !fileObj.IsFile(file) {
-			return nil, util.NewError(fmt.Sprintf("文件 %s 不存在", file))
+// Create 创建图像
+func (i *Image) Create() (*imagick.MagickWand, error) {
+	if i.File != "" {
+		i.File = fmt.Sprintf("%s/%s", i.Path, i.File)
+		if !fileObj.IsFile(i.File) {
+			return nil, util.NewError(fmt.Sprintf("文件 %s 不存在", i.File))
 		}
 	}
 	imageMW := imagick.NewMagickWand()
 
-	if file != "" {
-		if err := imageMW.ReadImage(file); err != nil {
+	if i.File != "" {
+		if err := imageMW.ReadImage(i.File); err != nil {
 			return nil, util.NewError(fmt.Sprintf("imageMW.ReadImageBlob:%s", err.Error()))
 		}
+		if err := imageMW.ResizeImage(i.ViewSize.Width, i.ViewSize.Height, imagick.FILTER_LANCZOS); err != nil {
+			return nil, util.NewError(fmt.Sprintf("imageMW.ResizeImage:%s", err.Error()))
+		}
 	} else {
-		if err := imageMW.ReadImageBlob(util.TransparentPNG()); err != nil {
-			return nil, util.NewError(fmt.Sprintf("imageMW.ReadImageBlob:%s", err.Error()))
+		if err := imageMW.NewImage(i.ViewSize.Width, i.ViewSize.Height, OpacityPixel()); err != nil {
+			return nil, util.NewError(fmt.Sprintf("imageMW.NewImage:%s", err.Error()))
 		}
 	}
-	fmt.Println("size", size)
 	if err := imageMW.SetImageFormat("PNG"); err != nil {
 		return nil, util.NewError("imageMW.SetImageFormat", err.Error())
 	}
-	if err := imageMW.ResizeImage(size.Width, size.Height, imagick.FILTER_LANCZOS); err != nil {
-		return nil, util.NewError("imageMW.SetSize", err.Error())
-	}
 
-	if background != "" {
-		if err := imageMW.SetImageBackgroundColor(Pixel(background)); err != nil {
+	if i.Background != "" {
+		if err := imageMW.SetImageBackgroundColor(Pixel(i.Background)); err != nil {
 			return nil, util.NewError(fmt.Sprintf("imageMW.SetImageBackgroundColor:%s", err.Error()))
 		}
 	}
-
+	if i.Angle > 0 {
+		if err := imageMW.RotateImage(OpacityPixel(), i.Angle); err != nil {
+			return nil, util.NewError(fmt.Sprintf("imageMW.RotateImage:%s", err.Error()))
+		}
+		fmt.Println(imageMW.GetImageWidth(), i.ViewSize.Width, imageMW.GetImageHeight(), i.ViewSize.Height)
+		alignX := float64(imageMW.GetImageWidth()-i.ViewSize.Width) / 2
+		alignY := float64(imageMW.GetImageHeight()-i.ViewSize.Height) / 2
+		i.SetPoint(i.ViewPoint.X-alignX, i.ViewPoint.Y-alignY)
+	}
 	return imageMW, nil
 }
 
-func (i *Image) Thumb(file string, size *Size) (string, error) {
-	if !fileObj.IsFile(file) {
-		return "", util.NewError(fmt.Sprintf("文件 %s 不存在", file))
+// Thumb 生成缩略图
+func (i *Image) Thumb() (*imagick.MagickWand, error) {
+	if !fileObj.IsFile(i.File) {
+		return nil, util.NewError(fmt.Sprintf("文件 %s 不存在", i.File))
 	}
 
 	mw := imagick.NewMagickWand()
-	defer mw.Destroy()
 
 	var err error
 
-	if err = mw.ReadImage(file); err != nil {
-		return "", err
+	if err = mw.ReadImage(i.File); err != nil {
+		return nil, util.NewError("mw.ReadImage err :", err.Error())
 	}
 
 	width := mw.GetImageWidth()
 	height := mw.GetImageHeight()
 
 	imageScale := NewScale(width, height, 1.0)
-	scale := imageScale.MinRatio(size.Width, size.Height)
+	scale := imageScale.MinRatio(i.ViewSize.Width, i.ViewSize.Height)
 
 	if err = mw.ResizeImage(scale.Width, scale.Height, imagick.FILTER_LANCZOS); err != nil {
-		return "", err
-	}
-	//fmt.Println("DisplayImage:", os.Getenv("DISPLAY"))
-	//err = mw.DisplayImage(os.Getenv("DISPLAY"))
-
-	destFile := i.CacheFile(file, mw.GetImageFormat())
-	if err = mw.WriteImage(destFile); err != nil {
-		return "", err
+		return nil, util.NewError("mw.ResizeImage err :", err.Error())
 	}
-	return destFile, nil
+	return mw, nil
 }
 
-func (i *Image) Cut(file string, size *Size, position string) (string, error) {
+// Cut 裁切图片,先将图片进行按最大比例缩放后再裁切,使裁切的图片显示更多内容
+func (i *Image) Cut(position string) (*imagick.MagickWand, error) {
 	if position == "" {
 		position = "left"
 	}
 
 	mw := imagick.NewMagickWand()
-	defer mw.Destroy()
-	var err error
-	err = mw.ReadImage(file)
 
-	if err != nil {
-		return "", err
+	if err := mw.ReadImage(i.File); err != nil {
+		return nil, util.NewError("mw.ReadImage err : ", err.Error())
 	}
 	sourceWidth := mw.GetImageWidth()
 	sourceHeight := mw.GetImageHeight()
 	imageScale := NewScale(sourceWidth, sourceHeight, 1.0)
 
-	scale := imageScale.MaxRatio(size.Width, size.Height)
+	scale := imageScale.MaxRatio(i.ViewSize.Width, i.ViewSize.Height)
 
-	if err = mw.ResizeImage(scale.Width, scale.Height, imagick.FILTER_LANCZOS); err != nil {
-		return "", err
+	if err := mw.ResizeImage(scale.Width, scale.Height, imagick.FILTER_LANCZOS); err != nil {
+		return nil, util.NewError("mw.ResizeImage err : ", err.Error())
 	}
-	x, y, width, height := i.Position(position, scale.Width, scale.Height, size)
+	x, y, width, height := i.Position(position, scale.Width, scale.Height, i.ViewSize)
 
-	if err = mw.CropImage(width, height, x, y); err != nil {
-		return "", err
+	if err := mw.CropImage(width, height, x, y); err != nil {
+		return nil, util.NewError("mw.CropImage err : ", err.Error())
 	}
-
-	destFile := i.CacheFile(file, mw.GetImageFormat())
-	if err = mw.WriteImage(destFile); err != nil {
-		return "", err
-	}
-
-	return destFile, nil
+	return mw, nil
 }
 
-func (i *Image) Resize(image string, width, height uint, angle int) (*imagick.MagickWand, error) {
+// Resize 缩放
+func (i *Image) Resize() (*imagick.MagickWand, error) {
 	imageMV := imagick.NewMagickWand()
 
-	if err := imageMV.ReadImage(image); err != nil {
+	if err := imageMV.ReadImage(i.File); err != nil {
 		return nil, util.NewError("Resize", "ReadImage", err.Error())
 	}
 
-	if err := imageMV.ResizeImage(width, height, imagick.FILTER_LANCZOS); err != nil {
+	if err := imageMV.ResizeImage(i.ViewSize.Width, i.ViewSize.Height, imagick.FILTER_LANCZOS); err != nil {
 		return nil, util.NewError("Resize", "ResizeImage", err.Error())
 	}
-
-	if err := imageMV.SetSize(width, height); err != nil {
-		return nil, util.NewError("Resize", "SetSize", err.Error())
+	/*
+		if err := imageMV.SetSize(i.ViewSize.Width, i.ViewSize.Height); err != nil {
+			return nil, util.NewError("Resize", "SetSize", err.Error())
+		}
+	*/
+	if i.Angle > 0 {
+		if err := imageMV.RotateImage(OpacityPixel(), i.Angle); err != nil {
+			return nil, util.NewError("Resize", "ResizeImage", err.Error())
+		}
+		alignX := float64(imageMV.GetImageWidth()-i.ViewSize.Width) / 2
+		alignY := float64(imageMV.GetImageHeight()-i.ViewSize.Height) / 2
+		i.ViewPoint.X = alignX
+		i.ViewPoint.Y = alignY
 	}
-
 	return imageMV, nil
 }
-
-// 计算 裁切的x、y及宽高
-func (i *Image) Position(position string, width, height uint, size *Size) (dx, dy int, dWidth, dHeight uint) {
-	x := 0
-	y := 0
-	scale := 1.0
-
-	if width < size.Width {
-		scale = CustomScale(width, size.Width)
-		width = size.Width
-	}
-	if height < size.Height {
-		scale = CustomScale(height, size.Height)
-		height = size.Height
-	}
-	paddingLeft := width - size.Width
-	paddingTop := height - size.Height
-
-	if position == "right" {
-		x = int(paddingLeft)
-	}
-	if position == "center" {
-		x = int(paddingLeft / 2)
-		y = int(paddingTop / 2)
-	}
-
-	if position == "right|center" {
-		x = int(paddingLeft)
-		y = int(paddingTop / 2)
-	}
-	if position == "left|center" {
-		y = int(paddingTop / 2)
-	}
-	return x, y, uint(float64(size.Width) * scale), uint(float64(size.Height) * scale)
-}

+ 60 - 14
util/image/poster.go

@@ -9,13 +9,32 @@ import (
 )
 
 type Poster struct {
-	ctx context.Context
+	ctx       context.Context
+	Path      string
+	CachePath string
 }
 
 func NewPoster(ctx context.Context) *Poster {
-	return &Poster{ctx: ctx}
+	poster := &Poster{ctx: ctx}
+	poster.Path = util.Getwd()
+	poster.CachePath = fmt.Sprintf("%s/cache", util.Getwd())
+	return poster
 }
 
+func (p *Poster) Image(item Items) (*imagick.MagickWand, Items, error) {
+	var updated Items
+	image := NewImage(p.Path)
+	image.SetCache(p.CachePath)
+	image.SetFile(item.Value)
+	image.SetSize(item.Width, item.Height)
+	image.SetAngle(float64(item.Angle))
+	image.SetPoint(float64(item.X), float64(item.Y))
+	drawMw, err := image.Create()
+	updated = item
+	updated.X = int(image.ViewPoint.X)
+	updated.Y = int(image.ViewPoint.Y)
+	return drawMw, updated, err
+}
 func (p *Poster) Text(item Items) (*imagick.MagickWand, Items, error) {
 	var updated Items
 	item.Value = strings.Replace(item.Value, "  ", " ", -1)
@@ -24,7 +43,7 @@ func (p *Poster) Text(item Items) (*imagick.MagickWand, Items, error) {
 	txt.SetColor(item.Color)
 	txt.SetBackground(item.Background)
 	txt.SetSize(item.Width, item.Height)
-	txt.SetPoint(float64(item.X), float64(item.Y))
+	txt.SetViewPoint(float64(item.X), float64(item.Y))
 	txt.SetAlign(*item.Align)
 	txt.SetBius(*item.Bius)
 	txt.SetAngle(int64(item.Angle))
@@ -34,8 +53,8 @@ func (p *Poster) Text(item Items) (*imagick.MagickWand, Items, error) {
 	mw, err := txt.Create()
 
 	updated = item
-	updated.X = int(txt.Coordinate.X)
-	updated.Y = int(txt.Coordinate.Y)
+	updated.X = int(txt.ViewPoint.X)
+	updated.Y = int(txt.ViewPoint.Y)
 	updated.Width = txt.ViewSize.Width
 	updated.Height = txt.ViewSize.Height
 	return mw, updated, err
@@ -43,16 +62,37 @@ func (p *Poster) Text(item Items) (*imagick.MagickWand, Items, error) {
 
 func (p *Poster) QRCode(item Items) (*imagick.MagickWand, Items, error) {
 	var updated Items
-	cachePath := fmt.Sprintf("%s/cache", util.Getwd())
 	qr := NewQR()
+	qr.SetCache(p.CachePath)
 	qr.SetValue(item.Value)
 	qr.SetSize(item.Width, item.Height)
 	qr.SetBackgroundColor(item.Background)
 	qr.SetFrontColor(item.Color)
-	qr.SetCache(cachePath)
+	qr.SetViewPoint(float64(item.X), float64(item.Y))
+	qr.SetAngle(float64(item.Angle))
 	mw, err := qr.Create()
 
 	updated = item
+	updated.X = int(qr.ViewPoint.X)
+	updated.Y = int(qr.ViewPoint.Y)
+
+	return mw, updated, err
+}
+
+func (p *Poster) Avatar(item Items) (*imagick.MagickWand, Items, error) {
+	var updated Items
+
+	avatar := NewAvatar()
+	avatar.SetBorder(1)
+	avatar.SetBackgroundColor(item.Background)
+	avatar.SetAvatar(item.Value)
+	avatar.SetSize(item.Width, item.Height)
+	avatar.SetViewPoint(float64(item.X), float64(item.Y))
+	mw, err := avatar.Create()
+
+	updated = item
+	updated.X = int(avatar.ViewPoint.X)
+	updated.Y = int(avatar.ViewPoint.Y)
 	return mw, updated, err
 }
 
@@ -64,16 +104,20 @@ type PosterMW struct {
 }
 
 func (p *Poster) Create(json PosterJson) (*imagick.MagickWand, error) {
-	cachePath := fmt.Sprintf("%s/cache", util.Getwd())
 	image := NewImage(util.Getwd())
-	image.SetCache(cachePath)
+	image.SetCache(p.Path)
+	image.SetCache(p.CachePath)
 	if json.Use == "bgUrl" {
 		json.Background = ""
 	}
 	if json.Use == "bgc" {
 		json.BackgroundUrl = ""
 	}
-	poster, err := image.Create(json.BackgroundUrl, json.Background, *NewSize(json.Width, json.Height))
+	image.SetSize(json.Width, json.Height)
+	image.SetPoint(0, 0)
+	image.SetBackground(json.Background)
+	image.SetFile(json.BackgroundUrl)
+	poster, err := image.Create()
 
 	if err != nil {
 		return nil, err
@@ -91,12 +135,16 @@ func (p *Poster) Create(json PosterJson) (*imagick.MagickWand, error) {
 			if item.Type == "text" {
 				drawMW, item, drawErr = p.Text(item)
 			}
+			// 图片旋转还未做
 			if item.Type == "image" {
-				drawMW, drawErr = image.Resize(item.Value, item.Width, item.Height, item.Angle)
+				drawMW, item, drawErr = p.Image(item)
 			}
 			if item.Type == "qrcode" {
 				drawMW, item, drawErr = p.QRCode(item)
 			}
+			if item.Type == "avatar" {
+				drawMW, item, drawErr = p.Avatar(item)
+			}
 			destChan <- &PosterMW{Index: index, Image: drawMW, Item: item, Err: drawErr}
 		}(item, index)
 	}
@@ -110,11 +158,9 @@ func (p *Poster) Create(json PosterJson) (*imagick.MagickWand, error) {
 		draw := response[i].Image
 		errDraw := response[i].Err
 		if errDraw != nil {
+			fmt.Println("Response err", errDraw.Error())
 			continue
 		}
-		if response[i].Item.Type == "text" {
-			fmt.Println(fmt.Sprintf("response[i].Item : %+v", response[i].Item))
-		}
 		if errComposite := poster.CompositeImage(draw, imagick.COMPOSITE_OP_OVER, true, response[i].Item.X, response[i].Item.Y); errComposite != nil {
 			fmt.Println("poster.CompositeImage text err : ", errComposite.Error())
 			continue

+ 35 - 33
util/image/qr.go

@@ -1,12 +1,10 @@
 package image
 
 import (
-	"fmt"
 	"github.com/skip2/go-qrcode"
 	"gopkg.in/gographics/imagick.v3/imagick"
 	"icloudapp.cn/tools/util"
 	"image/color"
-	"sync"
 )
 
 type QR struct {
@@ -14,16 +12,15 @@ type QR struct {
 	Value           string
 	FrontColor      string
 	BackgroundColor string
-	QRSize          *Size
+	ViewSize        *Size
+	ViewPoint       *Point
+	Angle           float64
 }
 
-var QROnce sync.Once
-var QRInstance *QR
-
 func NewQR() *QR {
-	QROnce.Do(func() {
-		QRInstance = &QR{}
-	})
+	QRInstance := &QR{}
+	QRInstance.SetSize(0, 0)
+	QRInstance.SetViewPoint(0, 0)
 	return QRInstance
 }
 
@@ -43,11 +40,17 @@ func (q *QR) SetBackgroundColor(backgroundColor string) {
 	q.BackgroundColor = backgroundColor
 }
 func (q *QR) SetSize(width, height uint) {
-	q.QRSize = NewSize(width, height)
+	q.ViewSize = NewSize(width, height)
+}
+func (q *QR) SetViewPoint(x, y float64) {
+	q.ViewPoint = NewPoint(x, y)
+}
+
+// SetAngle 设置旋转角度
+func (q *QR) SetAngle(angle float64) {
+	q.Angle = angle
 }
 func (q *QR) Create() (*imagick.MagickWand, error) {
-	//file := fmt.Sprintf("%s.png", util.Sign(q.Value, "QR"))
-	file := q.CacheFile(util.Sign(q.Value, "QR"), "png")
 	if q.BackgroundColor == "" {
 		q.BackgroundColor = "#000"
 	}
@@ -57,45 +60,44 @@ func (q *QR) Create() (*imagick.MagickWand, error) {
 	_, background, _ := Alpha(q.BackgroundColor)
 	_, front, _ := Alpha(q.FrontColor)
 
-	background.A = 1
 	backgroundColor := color.RGBA{R: background.R, G: background.G, B: background.B, A: background.A}
-
-	front.A = 1
 	frontColor := color.RGBA{R: front.R, G: front.G, B: front.B, A: front.A}
 
-	if q.QRSize.Width == 0 {
-		q.QRSize.Width = 200
+	if q.ViewSize.Width == 0 {
+		q.ViewSize.Width = 200
 	}
-
 	qr, err := qrcode.New(q.Value, qrcode.Medium)
 	if err != nil {
 		return nil, err
 	}
 
-	qr.Image(int(q.QRSize.Width))
 	qr.DisableBorder = true
 
 	qr.ForegroundColor = color.RGBA{R: 0x33, G: 0x33, B: 0x66, A: 0xff}
 	qr.BackgroundColor = color.RGBA{R: 0xef, G: 0xef, B: 0xef, A: 0xff}
 
-	//qr.ForegroundColor = frontColor
-	//qr.BackgroundColor = backgroundColor
-	fmt.Println("background", backgroundColor, frontColor)
+	qr.ForegroundColor = frontColor
+	qr.BackgroundColor = backgroundColor
 
-	if err = qr.WriteFile(int(q.QRSize.Width), file); err != nil {
-		return nil, util.NewError("qrcode.WriteColorFile", err.Error())
+	qrByte, err := qr.PNG(int(q.ViewSize.Width))
+	if err != nil {
+		return nil, util.NewError("qr.PNG err : ", err.Error())
 	}
-	/*if err := qrcode.WriteColorFile(q.Value, qrcode.Medium, int(q.QRSize.Width), color.White, color.Black, file); err != nil {
-		return nil, util.NewError("qrcode.WriteColorFile", err.Error())
-	}*/
-
 	qrWM := imagick.NewMagickWand()
-	if err = qrWM.ReadImage(file); err != nil {
-		return nil, util.NewError("qrWM.SetImageFilename", err.Error())
+	if err = qrWM.ReadImageBlob(qrByte); err != nil {
+		return nil, util.NewError("qrWM.SetImageFilename err : ", err.Error())
 	}
-	if err = qrWM.SetSize(q.QRSize.Width, q.QRSize.Height); err != nil {
-		return nil, util.NewError("qrWM.SetSize", err.Error())
+	if err = qrWM.SetSize(q.ViewSize.Width, q.ViewSize.Height); err != nil {
+		return nil, util.NewError("qrWM.SetSize err : ", err.Error())
+	}
+	if q.Angle > 0 {
+		if err = qrWM.RotateImage(OpacityPixel(), q.Angle); err != nil {
+			return nil, util.NewError("qrWM.RotateImage err : ", err.Error())
+		}
+		alignX := float64(qrWM.GetImageWidth()-q.ViewSize.Width) / 2
+		alignY := float64(qrWM.GetImageHeight()-q.ViewSize.Height) / 2
+		q.ViewPoint.X = q.ViewPoint.X - alignX
+		q.ViewPoint.Y = q.ViewPoint.Y - alignY
 	}
-
 	return qrWM, nil
 }

+ 46 - 55
util/image/text.go

@@ -37,7 +37,7 @@ type Text struct {
 	//对齐方式 left center right
 	Align string
 	//坐标
-	Coordinate *Point
+	ViewPoint *Point
 	//文字区域大小
 	ViewSize *Size
 
@@ -46,13 +46,13 @@ type Text struct {
 
 // NewText text对象
 func NewText(font string, fontSize float64, fontFamily string) *Text {
-	t := &Text{
+	TextInstance := &Text{
 		Font:       font,
 		FontSize:   fontSize,
 		FontFamily: fontFamily,
 	}
-	t.Clean()
-	return t
+	TextInstance.Clean()
+	return TextInstance
 }
 
 func (t *Text) SetText(text string) {
@@ -133,8 +133,8 @@ func (t *Text) SetAlign(align string) {
 	t.Align = align
 }
 
-func (t *Text) SetPoint(x, y float64) {
-	t.Coordinate = NewPoint(x, y)
+func (t *Text) SetViewPoint(x, y float64) {
+	t.ViewPoint = NewPoint(x, y)
 }
 
 func (t *Text) SetSize(w, h uint) {
@@ -154,7 +154,7 @@ func (t *Text) Create() (*imagick.MagickWand, error) {
 	}
 
 	if t.FontSize == 0 {
-		t.FontSize = 12
+		t.FontSize = 20
 	}
 
 	txtDraw.SetFontSize(t.FontSize)
@@ -166,7 +166,6 @@ func (t *Text) Create() (*imagick.MagickWand, error) {
 	}
 	if t.Color != "" {
 		tColor := Pixel(t.Color)
-		//tColor.SetOpacity(0)
 		txtDraw.SetFillColor(tColor)
 	}
 
@@ -180,7 +179,7 @@ func (t *Text) Create() (*imagick.MagickWand, error) {
 
 	txtDraw.SetFontStyle(imagick.STYLE_NORMAL)
 	if t.Bold {
-		//txtDraw.SetStrokeWidth(1)
+		txtDraw.SetStrokeWidth(1)
 		txtDraw.SetFontStyle(imagick.STYLE_OBLIQUE)
 	}
 
@@ -190,39 +189,41 @@ func (t *Text) Create() (*imagick.MagickWand, error) {
 
 	if t.Background != "" {
 		tb := Pixel(t.Background)
-		//tb.SetOpacity(0)
 		txtDraw.SetTextUnderColor(tb)
 	}
 	txtDraw.SetTextEncoding("UTF-8")
 
 	if t.Wrap {
 		txtArr := t.WapText(txtDraw, t.Text, t.Wrap)
-		fmt.Println("txtArr", txtArr)
+		t.Text = strings.Join(txtArr, "\n")
 	}
 
 	//透明图片
-	transPNG := util.TransparentPNG()
 	subMw := imagick.NewMagickWand()
-
-	if err := subMw.ReadImageBlob(transPNG); err != nil {
-		return nil, util.NewError("subMw.ReadImageBlob", err.Error())
+	var mwBackground *imagick.PixelWand
+	if t.Background == "" {
+		mwBackground = OpacityPixel()
+	} else {
+		mwBackground = Pixel(t.Background)
 	}
 
+	if err := subMw.NewImage(t.ViewSize.Width, t.ViewSize.Height, mwBackground); err != nil {
+		return nil, util.NewError("subMw.NewImage", err.Error())
+	}
 	if err := subMw.SetImageFormat("PNG"); err != nil {
 		return nil, util.NewError("subMw.SetImageFormat", err.Error())
 	}
-	if err := subMw.ResizeImage(t.ViewSize.Width, t.ViewSize.Height, imagick.FILTER_LANCZOS); err != nil {
-		return nil, util.NewError("subMw.ResizeImage", err.Error())
-	}
 
 	metrics := subMw.QueryFontMetrics(txtDraw, t.Text)
 	alignX := 0.0
 	if t.Align == "center" {
-		alignX = float64(int(((float64(t.ViewSize.Width)-metrics.TextWidth)/2)*100)) / 100
+		txtDraw.SetTextAlignment(imagick.ALIGN_CENTER)
+		alignX = float64(t.ViewSize.Width) / 2
 	}
 
 	if t.Align == "right" {
-		alignX = float64(t.ViewSize.Width) - metrics.TextWidth
+		txtDraw.SetTextAlignment(imagick.ALIGN_RIGHT)
+		alignX = float64(t.ViewSize.Width)
 	}
 
 	if t.Strike {
@@ -231,42 +232,30 @@ func (t *Text) Create() (*imagick.MagickWand, error) {
 	}
 
 	txtDraw.Annotation(alignX, metrics.Ascender, t.Text)
-	if t.Background != "" {
-		if err := subMw.SetImageBackgroundColor(Pixel(t.Background)); err != nil {
-			return nil, util.NewError("subMw.SetImageBackgroundColor", err.Error())
-		}
-	}
+
 	if err := subMw.DrawImage(txtDraw); err != nil {
 		return nil, util.NewError("subMw.DrawImage", err.Error())
 	}
 
 	parentMw := imagick.NewMagickWand()
 
-	if err := parentMw.ReadImageBlob(transPNG); err != nil {
-		return nil, util.NewError("parentMw.ReadImageBlob", err.Error())
+	if err := parentMw.NewImage(t.ViewSize.Width, t.ViewSize.Height, mwBackground); err != nil {
+		return nil, util.NewError("subMw.NewImage", err.Error())
 	}
-	if err := parentMw.ResizeImage(t.ViewSize.Width, t.ViewSize.Height, imagick.FILTER_LANCZOS); err != nil {
-		return nil, util.NewError("subMw.ResizeImage", err.Error())
-	}
-	if err := parentMw.SetSize(t.ViewSize.Width, t.ViewSize.Height); err != nil {
-		return nil, util.NewError("subMw.SetSize", err.Error())
-	}
-
 	if err := parentMw.CompositeImage(subMw, imagick.COMPOSITE_OP_OVER, true, 0, 0); err != nil {
 		return nil, util.NewError("parentMw.CompositeImage ", err.Error())
 	}
-	pixBackground := Pixel("#ffffffff")
-	if err := parentMw.RotateImage(pixBackground, float64(t.Angle)); err != nil {
+	if err := parentMw.RotateImage(OpacityPixel(), float64(t.Angle)); err != nil {
 		return nil, util.NewError("subMw.RotateImage", err.Error())
 	}
 	//由于图片旋转,图片的长宽和坐标位置发生变化,所以需要重置坐标位置
 	if t.Angle > 0 {
 		width := parentMw.GetImageWidth()
 		height := parentMw.GetImageHeight()
-		y := t.Coordinate.Y - float64(height)/3 + float64(t.ViewSize.Height)/2
-		x := t.Coordinate.X - float64(width)/2 + float64(t.ViewSize.Width)/2
-		t.Coordinate.X = x
-		t.Coordinate.Y = y
+		y := t.ViewPoint.Y - float64(height)/2 + float64(t.ViewSize.Height)/2
+		x := t.ViewPoint.X - float64(width)/2 + float64(t.ViewSize.Width)/2
+		t.ViewPoint.X = x
+		t.ViewPoint.Y = y
 		t.ViewSize.Width = width
 		t.ViewSize.Height = height
 	}
@@ -276,28 +265,30 @@ func (t *Text) Create() (*imagick.MagickWand, error) {
 
 func (t *Text) WapText(dw *imagick.DrawingWand, text string, autoWrap bool) []string {
 	queryMw := imagick.NewMagickWand()
+	if err := queryMw.NewImage(t.ViewSize.Width, t.ViewSize.Height, OpacityPixel()); err != nil {
+		return []string{}
+	}
 	metrics := queryMw.QueryFontMetrics(dw, t.Text)
-	fmt.Println("metrics", metrics)
 	var lines []string
 	if uint(metrics.TextWidth) <= t.ViewSize.Width {
 		lines = append(lines, text)
+
+		return lines
 	}
-	if autoWrap {
-		txtRune := []rune(text)
-		var tmpRune []string
-		for i, v := range txtRune {
+	txtRune := []rune(text)
+	var tmpRune []string
+	for _, v := range txtRune {
+		tmpRune = append(tmpRune, string(v))
+		metrics = queryMw.QueryFontMetrics(dw, strings.Join(tmpRune, ""))
+		if uint(metrics.TextWidth) >= t.ViewSize.Width {
+			lines = append(lines, strings.Join(tmpRune[0:len(tmpRune)-1], ""))
+			tmpRune = []string{}
 			tmpRune = append(tmpRune, string(v))
-			metrics = queryMw.QueryFontMetrics(dw, strings.Join(tmpRune, ""))
-			if uint(metrics.TextWidth) > t.ViewSize.Width {
-				fmt.Println("tmpRune", tmpRune)
-				lines = append(lines, strings.Join(tmpRune[0:i-1], ""))
-				tmpRune = []string{}
-			}
-		}
-		if len(tmpRune) > 0 {
-			lines = append(lines, strings.Join(tmpRune, ""))
 		}
 	}
+	if len(tmpRune) > 0 {
+		lines = append(lines, strings.Join(tmpRune, ""))
+	}
 	return lines
 }
 
@@ -305,7 +296,7 @@ func (t *Text) Clean() {
 	t.SetColor("#ffffff00")
 	t.SetBackground("#ffffffff")
 	t.SetSize(0, 0)
-	t.SetPoint(0, 0)
+	t.SetViewPoint(0, 0)
 	t.SetAlign("left")
 	t.SetBius("")
 	t.SetAngle(0)

+ 2 - 4
util/jobs/test_cron.go

@@ -1,13 +1,11 @@
 package jobs
 
-import "fmt"
-
 type TestJob struct {
 	Id   int
 	Name string
 }
 
 func (this TestJob) Run() {
-	fmt.Println(this.Id, this.Name)
-	fmt.Println("testJob1...")
+	//fmt.Println(this.Id, this.Name)
+	//fmt.Println("testJob1...")
 }