package utils import ( "bytes" "encoding/base64" "errors" "fmt" "image" "image/color" "image/draw" "image/png" "math/rand" "net/http" "os" "strconv" "time" "github.com/nfnt/resize" ) const ( ImgURL = "https://cdns.fontree.cn/fonchain-main/test/image/artwork/config/slidCode_%d.jpg" ImgPath = "./code/%d.jpg" //本地路径,暂不使用 ImgPathPng = "./code/%d.png" //本地路径,暂不使用 ) // 生成指定范围内的随机数 func GetNonceByRange(start, end int) int { rand.Seed(time.Now().UnixNano()) return rand.Intn(end-start+1) + start } // 获取缓冲图片,可能是通过URL或本地路径 func GetBufferedImage(place int, canvasWidth, canvasHeight int) (draw.Image, error) { startTime := time.Now() fmt.Println("") fmt.Println("") fmt.Println("") fmt.Println("1---") nonce := GetNonceByRange(1, 10) var imgURL string //place = 0 // 注意,这边的图片现在只使用url图片 if place == 1 { fmt.Println("3-1", time.Now().Sub(startTime)) imgURL = fmt.Sprintf(ImgURL, nonce) resp, err := http.Get(imgURL) if err != nil { return nil, err } fmt.Println("3-2", time.Now().Sub(startTime)) defer resp.Body.Close() img, ff, err := image.Decode(resp.Body) fmt.Println("----", ff) if err != nil { return nil, err } fmt.Println("3-3", time.Now().Sub(startTime)) canvasImage := ImageResize(img, canvasWidth, canvasHeight).(*image.RGBA) return canvasImage, nil } else { //从redis中获取 file, err := os.Open(fmt.Sprintf(ImgPathPng, nonce)) fmt.Println("4-1", time.Now().Sub(startTime)) if err != nil { return nil, err } defer file.Close() //img, format, err := image.Decode(file) img, err := png.Decode(file) format := "1" fmt.Println("", err) //img, err := jpeg.Decode(file) fmt.Println("4-1-1", time.Now().Sub(startTime), err) fmt.Println("4-2", time.Now().Sub(startTime), format) if err != nil { return nil, err } ss := ImageResize(img, canvasWidth, canvasHeight) canvasImage := ss.(*image.RGBA) return canvasImage, nil } return nil, errors.New("类型错误") } // 调整图片大小 func ImageResize(bufferedImage image.Image, width, height int) image.Image { return resize.Resize(uint(width), uint(height), bufferedImage, resize.Bilinear) } // CutByTemplate 根据模板裁剪图像 // 生成圆角正方形的滑块图像 func CutByTemplate(canvasImage, blockImage draw.Image, blockWidth, blockHeight, blockX, blockY int) { // 创建水印图像 waterImage := image.NewRGBA(image.Rect(0, 0, blockWidth, blockHeight)) blockData := getRoundedSquareData(blockWidth, blockHeight) // 遍历滑块区域,将滑块图案从背景中裁剪出来,并生成水印 for i := 0; i < blockWidth; i++ { for j := 0; j < blockHeight; j++ { switch blockData[i][j] { case 1: // 实心区域 blockImage.Set(i, j, canvasImage.At(blockX+i, blockY+j)) waterImage.Set(i, j, color.Black) case 2: // 半透明边缘(不再需要) origColor := canvasImage.At(blockX+i, blockY+j) r, g, b, _ := origColor.RGBA() alpha := uint8(180) blockImage.Set(i, j, color.NRGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), alpha}) waterImage.Set(i, j, color.NRGBA{0, 0, 0, alpha}) case 3: // 纯白边框区域 blockImage.Set(i, j, color.White) // 设置纯白色 waterImage.Set(i, j, color.White) default: // 透明区域 blockImage.Set(i, j, color.Transparent) waterImage.Set(i, j, color.Transparent) } } } // 在原始画布上清除滑块区域并设置透明背景 clearCanvasArea(canvasImage, blockData, blockX, blockY, blockWidth, blockHeight) } // 获取带纯白边框的平滑圆角正方形的块数据 func getRoundedSquareData(blockWidth, blockHeight int) [][]int { data := make([][]int, blockWidth) for i := range data { data[i] = make([]int, blockHeight) } // 设置圆角半径为正方形边长的 20% cornerRadius := float64(blockWidth) * 0.2 cornerRadiusSquared := cornerRadius * cornerRadius // 设置边框厚度为 1 像素 borderThickness := 1.0 // 纯白边框宽度为 1 像素 borderRadiusSquared := (cornerRadius - borderThickness) * (cornerRadius - borderThickness) for i := 0; i < blockWidth; i++ { for j := 0; j < blockHeight; j++ { // 处理四个角的圆角 if (i < int(cornerRadius) && j < int(cornerRadius)) || // 左上角 (i >= blockWidth-int(cornerRadius) && j < int(cornerRadius)) || // 右上角 (i < int(cornerRadius) && j >= blockHeight-int(cornerRadius)) || // 左下角 (i >= blockWidth-int(cornerRadius) && j >= blockHeight-int(cornerRadius)) { // 右下角 // 计算距离的平方,判断是否在圆角内 var dx, dy float64 if i < int(cornerRadius) { dx = float64(i) - cornerRadius } else { dx = float64(i) - float64(blockWidth) + cornerRadius } if j < int(cornerRadius) { dy = float64(j) - cornerRadius } else { dy = float64(j) - float64(blockHeight) + cornerRadius } distanceSquared := dx*dx + dy*dy if distanceSquared > cornerRadiusSquared { data[i][j] = 0 // 圆角外部区域 } else if distanceSquared > borderRadiusSquared { data[i][j] = 3 // 纯白边框区域 } else { data[i][j] = 1 // 实心区域 } } else { // 非圆角区域 if (i < int(borderThickness) || i >= blockWidth-int(borderThickness)) || // 左右边缘 (j < int(borderThickness) || j >= blockHeight-int(borderThickness)) { // 上下边缘 data[i][j] = 3 // 纯白边框区域 } else { data[i][j] = 1 // 实心区域 } } } } return data } // 随机生成一个不重叠的滑块区域 func generateNonOverlappingArea(canvasWidth, canvasHeight, blockX, blockY, blockWidth, blockHeight int) (int, int) { rand.Seed(time.Now().UnixNano()) var newX, newY int for { // 随机生成新区域的位置 newX = rand.Intn(canvasWidth - blockWidth) newY = rand.Intn(canvasHeight - blockHeight) // 检查新区域是否与原始区域重叠 if (newX+blockWidth <= blockX || newX >= blockX+blockWidth) && (newY+blockHeight <= blockY || newY >= blockY+blockHeight) { break } } return newX, newY } // 将滑块区域设为半透明,并生成不重叠的额外区域 func clearCanvasArea(canvasImage draw.Image, blockData [][]int, blockX, blockY, blockWidth, blockHeight int) { alpha := uint8(128) // 50% 透明度 // 设置原始滑块区域为半透明 for i := 0; i < blockWidth; i++ { for j := 0; j < blockHeight; j++ { if blockData[i][j] > 0 { // 获取原始像素的 RGB 值,并统一设置 Alpha 通道 originalColor := canvasImage.At(blockX+i, blockY+j) r, g, b, _ := originalColor.RGBA() // 设置新的颜色并固定透明度 semiTransparentColor := color.NRGBA{ R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: alpha, // 固定透明度 } // 应用半透明效果 canvasImage.Set(blockX+i, blockY+j, semiTransparentColor) } } } // 生成不重叠的新区域位置 canvasBounds := canvasImage.Bounds() newBlockX, newBlockY := generateNonOverlappingArea(canvasBounds.Max.X, canvasBounds.Max.Y, blockX, blockY, blockWidth, blockHeight) // 可以在新区域执行与原始区域相同的操作,或根据需求自定义 for i := 0; i < blockWidth; i++ { for j := 0; j < blockHeight; j++ { if blockData[i][j] > 0 { // 获取原始像素的 RGB 值,并统一设置 Alpha 通道 originalColor := canvasImage.At(newBlockX+i, newBlockY+j) r, g, b, _ := originalColor.RGBA() // 设置新的颜色并固定透明度 semiTransparentColor := color.NRGBA{ R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: alpha, // 固定透明度 } // 应用半透明效果 canvasImage.Set(newBlockX+i, newBlockY+j, semiTransparentColor) } } } } //// 清除画布上的滑块区域,使其为黑色背景 //func clearCanvasArea(canvasImage draw.Image, blockData [][]int, blockX, blockY, blockWidth, blockHeight int) { // alpha := uint8(128) // 50% 透明度 // // for i := 0; i < blockWidth; i++ { // for j := 0; j < blockHeight; j++ { // if blockData[i][j] > 0 { // // 获取原始像素的 RGB 值,并统一设置 Alpha 通道 // originalColor := canvasImage.At(blockX+i, blockY+j) // r, g, b, _ := originalColor.RGBA() // // // 设置新的颜色并固定透明度 // semiTransparentColor := color.NRGBA{ // R: uint8(r >> 8), // G: uint8(g >> 8), // B: uint8(b >> 8), // A: alpha, // 固定透明度 // } // // // 应用半透明效果 // canvasImage.Set(blockX+i, blockY+j, semiTransparentColor) // } // } // } //} //// 根据模板裁剪图像 // 生成拼图类型的滑块图像 有需要打开 //func CutByTemplate(canvasImage, blockImage draw.Image, blockWidth, blockHeight, blockRadius, blockX, blockY int) (faceY int) { // waterImage := image.NewRGBA(image.Rect(0, 0, blockWidth, blockHeight)) // var blockData [][]int // blockData, faceY = getBlockData(blockWidth, blockHeight, blockRadius, blockY) // // for i := 0; i < blockWidth; i++ { // for j := 0; j < blockHeight; j++ { // if blockData[i][j] == 1 { // // 设置水印背景为黑色 // waterImage.Set(i, j, color.Black) // // 从原始图像中获取像素 // blockImage.Set(i, j, canvasImage.At(blockX+i, blockY+j)) // // // 设置轮廓为白色 // if (i+1 < blockWidth && blockData[i+1][j] == 0) || // (j+1 < blockHeight && blockData[i][j+1] == 0) || // (i-1 >= 0 && blockData[i-1][j] == 0) || // (j-1 >= 0 && blockData[i][j-1] == 0) { // blockImage.Set(i, j, color.White) // waterImage.Set(i, j, color.White) // } // } else { // // 背景设为透明 // blockImage.Set(i, j, color.Transparent) // waterImage.Set(i, j, color.Transparent) // } // } // } // addBlockWatermark(canvasImage, waterImage, blockX, blockY) // return //} // //// 获取块数据,用以确定块的形状 //func getBlockData(blockWidth, blockHeight, blockRadius, blockY int) (blockData [][]int, faceY int) { // data := make([][]int, blockWidth) // for i := range data { // data[i] = make([]int, blockHeight) // } // rand.Seed(time.Now().UnixNano()) // // // 确定两个随机方向和凸/凹形状 // face1, face2 := rand.Intn(4), rand.Intn(4) // for face1 == face2 { // face2 = rand.Intn(4) // } // // shape := rand.Intn(2) // 0: 凸,1: 凹 // circle1 := getCircleCoords(face1, blockWidth, blockHeight, blockRadius) // circle2 := getCircleCoords(face2, blockWidth, blockHeight, blockRadius) // // if (face1 == 2 || face2 == 2) && shape == 1 { // faceY = blockY - blockRadius // } else { // faceY = blockY // } // // 计算轮廓 // radiusSquared := float64(blockRadius * blockRadius) // for i := 0; i < blockWidth; i++ { // for j := 0; j < blockHeight; j++ { // if i >= blockRadius && i < blockWidth-blockRadius && j >= blockRadius && j < blockHeight-blockRadius { // data[i][j] = 1 // } // if circle1 != nil && math.Pow(float64(i-circle1[0]), 2)+math.Pow(float64(j-circle1[1]), 2) <= radiusSquared { // data[i][j] = shape // } // if circle2 != nil && math.Pow(float64(i-circle2[0]), 2)+math.Pow(float64(j-circle2[1]), 2) <= radiusSquared { // data[i][j] = shape // } // } // } // return data, faceY //} // //// 根据方向获取圆心坐标 //func getCircleCoords(face, blockWidth, blockHeight, blockRadius int) []int { // switch face { // case 0: // return []int{blockWidth / 2, blockRadius} // 上 // case 1: // return []int{blockRadius, blockHeight / 2} // 左 // case 2: // return []int{blockWidth / 2, blockHeight - blockRadius - 1} // 下 // case 3: // return []int{blockWidth - blockRadius - 1, blockHeight / 2} // 右 // default: // return nil // } //} // //// 在画布图像上添加水印 //func addBlockWatermark(canvasImage, waterImage draw.Image, blockX, blockY int) { // for i := 0; i < waterImage.Bounds().Dx(); i++ { // for j := 0; j < waterImage.Bounds().Dy(); j++ { // if _, _, _, alpha := waterImage.At(i, j).RGBA(); alpha > 0 { // canvasImage.Set(blockX+i, blockY+j, waterImage.At(i, j)) // } // } // } //} // 将图像转换为Base64编码 func ToBase64(img image.Image, format string) string { buf := new(bytes.Buffer) switch format { case "png": png.Encode(buf, img) } return base64.StdEncoding.EncodeToString(buf.Bytes()) } // 计算绝对值 func Abs(a int) int { if a < 0 { return -a } return a } // 将字符串转换为整数 func ParseInt(s string) int { i, _ := strconv.Atoi(s) return i }