代码拉取完成,页面将自动刷新
package ximage
import (
"bytes"
"errors"
"fmt"
"gitee.com/xlm516/xtool/dbg"
xfile "gitee.com/xlm516/xtool/file"
xstring "gitee.com/xlm516/xtool/string"
"gitee.com/xlm516/xtool/sys"
"github.com/disintegration/imaging"
"github.com/foobaz/lossypng/lossypng"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"github.com/rwcarlsen/goexif/exif"
"golang.org/x/image/bmp"
"image"
"image/color"
"image/draw"
"image/gif"
"image/jpeg"
"image/png"
"io"
"io/ioutil"
"os"
"strings"
)
type RotateType int
const (
orientationUnspecified RotateType = iota
orientationNormal = 1
orientationFlipH = 2
orientationRotate180 = 3
orientationFlipV = 4
orientationTranspose = 5
orientationRotate270 = 6
orientationTransverse = 7
orientationRotate90 = 8
)
type ImageEx struct {
width int
height int
fontSize float64
img *image.NRGBA
orgImage image.Image
currFont *truetype.Font
}
type Circle struct {
p image.Point
r int
}
func (c *Circle) ColorModel() color.Model {
return color.AlphaModel
}
func (c *Circle) Bounds() image.Rectangle {
return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
}
func (c *Circle) At(x, y int) color.Color {
xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
if xx*xx+yy*yy < rr*rr {
return color.Alpha{255}
}
return color.Alpha{0}
}
type DrawTextOption struct {
FontColor color.Color
FontSize float64
}
func NewImageFromImage(fromImage image.Image) *ImageEx {
img := new(ImageEx)
img.width = fromImage.Bounds().Dx()
img.height = fromImage.Bounds().Dy()
img.init()
img.orgImage = fromImage
img.img, _ = fromImage.(*image.NRGBA)
return img
}
func NewImage(width, height int) *ImageEx {
img := new(ImageEx)
img.width = width
img.height = height
img.init()
return img
}
func (me *ImageEx) init() {
me.fontSize = 15
if me.width > 0 && me.height > 0 {
me.img = image.NewNRGBA(image.Rect(0, 0, me.width, me.height))
} else {
me.img = nil
}
}
func (me *ImageEx) DrawBackground(r, g, b int) {
if me.img == nil {
return
}
for x := 0; x < me.width; x++ {
for y := 0; y < me.height; y++ {
me.img.Set(x, y, color.RGBA{uint8(r), uint8(g), uint8(b), 255})
}
}
}
func (me *ImageEx) SetFont(font *truetype.Font) {
me.currFont = font
}
func (me *ImageEx) SetFontFile(filename string) error {
b, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
font, err := freetype.ParseFont(b)
if err != nil {
return err
}
me.currFont = font
return nil
}
func (me *ImageEx) SubImage(x, y, w, h int) image.Image {
if me.img == nil {
return nil
}
return me.img.SubImage(image.Rect(x, y, x+w, y+h))
}
func (me *ImageEx) SubImageByPos(x0, y0, x1, y1 int) image.Image {
if me.img == nil {
return nil
}
return me.img.SubImage(image.Rect(x0, y0, x1, y1))
}
func (me *ImageEx) DrawText(x int, y int, text string, fontColor color.Color, option *DrawTextOption) error {
if me.currFont == nil {
return errors.New("no set truetype font")
}
if me.img == nil {
fmt.Println("image is null")
return errors.New("image is null")
}
c := freetype.NewContext()
c.SetFont(me.currFont)
//c.SetDPI(72)
c.SetFontSize(me.fontSize)
c.SetClip(me.img.Bounds())
c.SetDst(me.img)
fontSize := me.fontSize
if fontColor == nil {
c.SetSrc(image.Black)
} else {
c.SetSrc(image.NewUniform(fontColor))
}
if option != nil {
if option.FontSize > 0 {
c.SetFontSize(option.FontSize)
fontSize = option.FontSize
}
if option.FontColor != nil {
c.SetSrc(image.NewUniform(option.FontColor))
}
}
pt := freetype.Pt(x, y+int(c.PointToFixed(fontSize).Ceil()))
_, err := c.DrawString(text, pt)
if err != nil {
return err
}
return nil
}
func RgbaToNRGBA(rgba image.Image) *image.NRGBA {
bounds := rgba.Bounds()
nrgba := image.NewNRGBA(bounds)
draw.Draw(nrgba, bounds, rgba, bounds.Min, draw.Src)
return nrgba
}
func (me *ImageEx) LoadFromBuffer(buf []byte) (image.Image, error) {
rd := bytes.NewReader(buf)
img, _, err := image.Decode(rd)
if err != nil {
me.img = nil
return nil, err
}
me.orgImage = img
me.img = RgbaToNRGBA(img)
return img, nil
}
func (me *ImageEx) LoadFromFile(filename string) (image.Image, error) {
img, err := me.loadFromFile(filename)
if err != nil {
me.img = nil
return nil, err
}
return img, err
}
func (me *ImageEx) loadFromFile(filename string) (image.Image, error) {
filename = sys.FormatDir(filename)
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
defer fh.Close()
img, _, err := image.Decode(fh)
return img, err
ext := xstring.GetFileExt(filename)
ext = strings.ToLower(ext)
var tmpImage image.Image
switch ext {
case ".bmp":
tmpImage, err = bmp.Decode(fh)
if err != nil {
return nil, err
}
case ".png":
tmpImage, err = png.Decode(fh)
if err != nil {
return nil, err
}
case ".gif":
tmpImage, err = gif.Decode(fh)
if err != nil {
return nil, err
}
case ".jpg", ".jpeg":
tmpImage, err = jpeg.Decode(fh)
if err != nil {
return nil, err
}
default:
return nil, errors.New("not support" + ext)
}
_, ok := tmpImage.(*image.NRGBA)
if ok {
}
return tmpImage, nil
}
func (me *ImageEx) SaveToFile(filename string, imgType string) error {
data := new(bytes.Buffer)
if imgType == "" {
ext := xstring.GetFileExt(filename)
if ext != "" {
ext = strings.ToLower(ext)
ext = ext[1:]
}
}
var img image.Image
img = me.img
if img == nil {
img = me.orgImage
}
if img == nil {
return errors.New("invalid image")
}
switch imgType {
case "jpg", ".jpg":
err := jpeg.Encode(data, img, nil)
if err != nil {
return err
}
case "gif", ".gif":
err := gif.Encode(data, img, nil)
if err != nil {
return err
}
default:
err := png.Encode(data, img)
if err != nil {
return err
}
}
err := ioutil.WriteFile(filename, data.Bytes(), 0666)
return err
}
func (me *ImageEx) SaveToFileJPG(filename string, quality int) error {
data := new(bytes.Buffer)
var opt *jpeg.Options
if quality > 0 {
opt = new(jpeg.Options)
opt.Quality = quality
}
var img image.Image
img = me.img
if img == nil {
img = me.orgImage
}
err := jpeg.Encode(data, img, opt)
if err != nil {
return err
}
err = ioutil.WriteFile(filename, data.Bytes(), 0666)
return err
}
func (me *ImageEx) SaveToBuffer(w io.Writer, imgType string) error {
var err error
var img image.Image
img = me.img
if img == nil {
img = me.orgImage
}
if img == nil {
return errors.New("invalid image")
}
switch imgType {
case "jpg":
err = jpeg.Encode(w, img, nil)
if err != nil {
return err
}
case "gif":
err = gif.Encode(w, img, nil)
if err != nil {
return err
}
default:
err = png.Encode(w, img)
if err != nil {
return err
}
}
return err
}
func (me *ImageEx) GetImage() image.Image {
return me.img
}
func (me *ImageEx) GetImageRGB() *image.NRGBA {
return me.img
}
type DrawImageOption struct {
Opacity float64
Center bool
}
func (me *ImageEx) DrawImage(x, y int, img image.Image, option *DrawImageOption) *image.NRGBA {
if me.img == nil || sys.IsNil(img) {
return nil
}
pos := image.Point{}
var tmpImage *image.NRGBA
Opacity := float64(-1)
Center := false
if option != nil {
Opacity = option.Opacity
if Opacity == 0 {
Opacity = -1
}
Center = option.Center
}
pos.X = x
pos.Y = y
if Opacity >= 0 {
if Center {
tmpImage = imaging.OverlayCenter(me.img, img, Opacity)
} else {
tmpImage = imaging.Overlay(me.img, img, pos, Opacity)
}
} else {
if Center {
tmpImage = imaging.PasteCenter(me.img, img)
} else {
tmpImage = imaging.Paste(me.img, img, pos)
}
}
me.img = tmpImage
return tmpImage
}
func (me *ImageEx) DrawImageByFile(x, y int, imageFile string, option *DrawImageOption) error {
ext := xstring.GetFileExt(imageFile)
ext = strings.ToLower(ext)
srcImage, err := me.loadFromFile(imageFile)
if err != nil {
return err
}
if srcImage != nil {
me.DrawImage(x, y, srcImage, option)
}
return nil
}
func (me *ImageEx) DrawCircle(src image.Image, srcX, srcY, r int, dstX, dstY int) {
if me.img == nil {
return
}
p := image.Point{srcX, srcY}
dstPt := image.Rect(dstX, dstY, dstX+r*2+1, dstY+r*2+1)
draw.DrawMask(me.img, dstPt, src, image.ZP, &Circle{p, r}, image.ZP, draw.Over)
}
func (me *ImageEx) Resize(width, height int) {
me.width = width
me.height = height
me.img = imaging.Resize(me.img, width, height, imaging.Lanczos)
}
func (me *ImageEx) ResizePercent(per int) {
if me.img == nil {
return
}
me.width = me.img.Bounds().Dx()
me.height = me.img.Bounds().Dy()
width := me.width * per / 100
height := me.height * per / 100
me.Resize(width, height)
}
func (me *ImageEx) DrawHLine(color color.Color, x, fromY, toY int) {
if me.img == nil {
return
}
if x >= me.img.Bounds().Dx() {
return
}
if fromY >= me.img.Bounds().Dy() {
return
}
if me.img.Bounds().Dy()-fromY >= toY {
toY = me.img.Bounds().Dy() - fromY
}
// 遍历画每一个点
for y := fromY; y <= toY; y++ {
me.img.Set(x, y, color)
}
}
func (me *ImageEx) DrawVLine(color color.Color, fromX, toX, y int) {
if me.img == nil {
return
}
if me.img == nil {
return
}
if y >= me.img.Bounds().Dy() {
return
}
if fromX >= me.img.Bounds().Dx() {
return
}
if me.img.Bounds().Dx()-fromX >= toX {
toX = me.img.Bounds().Dy() - fromX
}
// 遍历画每一个点
for x := fromX; x <= toX; x++ {
me.img.Set(x, y, color)
}
}
type PutPixel func(x, y int)
func abs(x int) int {
if x >= 0 {
return x
}
return -x
}
func (me *ImageEx) DrawLine(x0, y0, x1, y1 int, brush PutPixel) {
if me.img == nil {
return
}
if brush == nil {
brush = func(x, y int) {
me.img.Set(x, y, color.RGBA{uint8(x), uint8(y), 0, 255})
}
}
dx := abs(x1 - x0)
dy := abs(y1 - y0)
sx, sy := 1, 1
if x0 >= x1 {
sx = -1
}
if y0 >= y1 {
sy = -1
}
err := dx - dy
for {
brush(x0, y0)
if x0 == x1 && y0 == y1 {
return
}
e2 := err * 2
if e2 > -dy {
err -= dy
x0 += sx
}
if e2 < dx {
err += dx
y0 += sy
}
}
}
func Rotate90(img image.Image) *image.NRGBA {
return imaging.Rotate90(img)
}
func Rotate180(img image.Image) *image.NRGBA {
return imaging.Rotate180(img)
}
func Rotate270(img image.Image) *image.NRGBA {
return imaging.Rotate270(img)
}
func ReadOrientation(filename string) RotateType {
file, err := os.Open(filename)
if err != nil {
fmt.Println("failed to open file, err: ", err)
return 0
}
defer file.Close()
x, err := exif.Decode(file)
if err != nil {
//fmt.Println("failed to decode file, err: ", err)
return 0
}
orientation, err := x.Get(exif.Orientation)
if err != nil {
//fmt.Println("failed to get orientation, err: ", err)
return 0
}
orientVal, err := orientation.Int(0)
if err != nil {
fmt.Println("failed to convert type of orientation, err: ", err)
return 0
}
//fmt.Println("the value of photo orientation is :", orientVal)
return RotateType(orientVal)
}
type ImageOption struct {
JPGQuality int
PNGQuality int
CompressSize int64 // 最大图片文件的大小
WaterMark string // 水印
WaterX int
WaterY int
WaterOpacity float64
}
func imageSaveToFile(filename string, img image.Image, imgType string, opt *ImageOption) error {
if opt.WaterMark != "" {
fh, err := os.Open(opt.WaterMark)
if err == nil {
waterImage, _, err := image.Decode(fh)
fh.Close()
if err == nil {
tmpImage := imaging.Overlay(img, waterImage, image.Point{X: opt.WaterX, Y: opt.WaterY}, opt.WaterOpacity)
if tmpImage != nil {
img = tmpImage
}
}
}
}
data := new(bytes.Buffer)
switch imgType {
case "jpg", "jpeg":
var jpgOpt *jpeg.Options
if opt != nil && opt.JPGQuality > 0 {
jpgOpt = new(jpeg.Options)
jpgOpt.Quality = opt.JPGQuality
}
err := jpeg.Encode(data, img, jpgOpt)
if err != nil {
return err
}
case "gif":
err := gif.Encode(data, img, nil)
if err != nil {
return err
}
case "png":
if opt != nil && opt.PNGQuality > 0 {
img = lossypng.Compress(img, lossypng.NoConversion, opt.PNGQuality)
}
err := png.Encode(data, img)
if err != nil {
return err
}
default:
dbg.Dbg("image type: %s\n", imgType)
err := png.Encode(data, img)
if err != nil {
return err
}
}
xfile.Mkdir(xstring.GetFilePath(filename))
err := xfile.WriteFile(filename, data.Bytes(), 0666)
if err != nil {
fmt.Println("imageSaveToFile: ", err)
}
return err
}
func ResizeByWidth(srcFile string, dstFile string, width int, autoRotate bool, opt *ImageOption) (string, error) {
fh, err := os.Open(srcFile)
if err != nil {
return "", err
}
fileSize := int64(0)
st, err := fh.Stat()
if err == nil && st != nil {
fileSize = st.Size()
}
defer func() {
if fh != nil {
fh.Close()
}
}()
img, imgType, err := image.Decode(fh)
fh.Close()
fh = nil
if err != nil {
return "", err
}
rotateInfo := ReadOrientation(srcFile)
if rotateInfo > 0 && autoRotate {
switch rotateInfo {
case orientationRotate270: //90度图片旋转
img = Rotate270(img)
case orientationRotate180:
img = Rotate180(img)
case orientationRotate90:
img = Rotate90(img)
}
}
if img.Bounds().Dx() > width {
x := width
y := int(float64(img.Bounds().Dy()) / float64(img.Bounds().Dx()) * float64(width))
img = imaging.Resize(img, x, y, imaging.Lanczos)
err = imageSaveToFile(dstFile, img, imgType, opt)
if err != nil {
return imgType, err
}
return imgType, nil
} else {
if (opt.CompressSize > 0 && fileSize > opt.CompressSize) || opt.WaterMark != "" {
err = imageSaveToFile(dstFile, img, imgType, opt)
if err != nil {
return imgType, err
}
} else {
xfile.Mkdir(xstring.GetFilePath(dstFile))
err = xfile.CopyFile(dstFile, srcFile)
if err != nil {
return imgType, err
}
}
return imgType, nil
}
}
func SaveToFile(filename string, imgType string, img image.Image) error {
data := new(bytes.Buffer)
if imgType == "" {
ext := xstring.GetFileExt(filename)
if ext != "" {
ext = strings.ToLower(ext)
ext = ext[1:]
}
}
if img == nil {
return errors.New("invalid image")
}
switch imgType {
case "jpg", ".jpg":
err := jpeg.Encode(data, img, nil)
if err != nil {
return err
}
case "gif", ".gif":
err := gif.Encode(data, img, nil)
if err != nil {
return err
}
default:
err := png.Encode(data, img)
if err != nil {
return err
}
}
err := xfile.WriteFile(filename, data.Bytes(), 0666)
return err
}
func SaveToBuffer(imgType string, img image.Image) ([]byte, error) {
data := new(bytes.Buffer)
if img == nil {
return nil, errors.New("invalid image")
}
switch imgType {
case "jpg", ".jpg":
err := jpeg.Encode(data, img, nil)
if err != nil {
return nil, err
}
case "gif", ".gif":
err := gif.Encode(data, img, nil)
if err != nil {
return nil, err
}
default:
err := png.Encode(data, img)
if err != nil {
return nil, err
}
}
return data.Bytes(), nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。