package skin_renderer import ( "bytes" "image" "image/color" "image/png" "math" ) // SkinRenderer 皮肤渲染器 type SkinRenderer struct { playerSkin image.Image isNewSkinType bool isAlex bool hdRatio int // 旋转参数 ratio float64 headOnly bool hR float64 // 水平旋转角度 vR float64 // 垂直旋转角度 hrh float64 // 头部水平旋转 vrll float64 // 左腿垂直旋转 vrrl float64 // 右腿垂直旋转 vrla float64 // 左臂垂直旋转 vrra float64 // 右臂垂直旋转 layers bool // 是否渲染第二层 // 计算后的三角函数值 cosAlpha, sinAlpha float64 cosOmega, sinOmega float64 // 边界 minX, maxX, minY, maxY float64 // 各部件的旋转角度 membersAngles map[string]angleSet // 可见面 visibleFaces map[string]faceVisibility frontFaces []string backFaces []string // 多边形 polygons map[string]map[string][]*Polygon } type angleSet struct { cosAlpha, sinAlpha float64 cosOmega, sinOmega float64 } type faceVisibility struct { front []string back []string } var allFaces = []string{"back", "right", "top", "front", "left", "bottom"} // NewSkinRenderer 创建皮肤渲染器 func NewSkinRenderer(ratio float64, headOnly bool, horizontalRotation, verticalRotation float64) *SkinRenderer { return &SkinRenderer{ ratio: ratio, headOnly: headOnly, hR: horizontalRotation, vR: verticalRotation, hrh: 0, vrll: 0, vrrl: 0, vrla: 0, vrra: 0, layers: true, } } // NewSkinRendererFull 创建带完整参数的皮肤渲染器 func NewSkinRendererFull(ratio float64, headOnly bool, hR, vR, hrh, vrll, vrrl, vrla, vrra float64, layers bool) *SkinRenderer { return &SkinRenderer{ ratio: ratio, headOnly: headOnly, hR: hR, vR: vR, hrh: hrh, vrll: vrll, vrrl: vrrl, vrla: vrla, vrra: vrra, layers: layers, } } // Render 渲染皮肤 func (r *SkinRenderer) Render(skinData []byte, isAlex bool) (image.Image, error) { // 解码皮肤图像 img, err := png.Decode(bytes.NewReader(skinData)) if err != nil { return nil, err } r.playerSkin = img r.isAlex = isAlex // 计算 HD 比例 sourceWidth := img.Bounds().Dx() sourceHeight := img.Bounds().Dy() // 防止内存溢出,限制最大尺寸 if sourceWidth > 256 { r.playerSkin = resizeImage(img, 256, sourceHeight*256/sourceWidth) } r.hdRatio = r.playerSkin.Bounds().Dx() / 64 // 检查是否为新版皮肤格式(64x64) if r.playerSkin.Bounds().Dx() == r.playerSkin.Bounds().Dy() { r.isNewSkinType = true } // 转换为 RGBA r.playerSkin = convertToRGBA(r.playerSkin) // 处理背景透明 r.makeBackgroundTransparent() // 计算角度 r.calculateAngles() // 确定可见面 r.facesDetermination() // 生成多边形 r.generatePolygons() // 部件旋转 r.memberRotation() // 创建投影 r.createProjectionPlan() // 渲染图像 return r.displayImage(), nil } // makeBackgroundTransparent 处理背景透明 func (r *SkinRenderer) makeBackgroundTransparent() { rgba, ok := r.playerSkin.(*image.RGBA) if !ok { return } // 检查左上角 8x8 区域是否为纯色 var tempColor color.RGBA needRemove := true first := true for y := 0; y < 8; y++ { for x := 0; x < 8; x++ { c := rgba.RGBAAt(x, y) // 如果已有透明度,不需要处理 if c.A < 128 { needRemove = false break } if first { tempColor = c first = false } else if c != tempColor { needRemove = false break } } if !needRemove { break } } if !needRemove { return } // 将该颜色设为透明 bounds := rgba.Bounds() for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { c := rgba.RGBAAt(x, y) if c.R == tempColor.R && c.G == tempColor.G && c.B == tempColor.B { rgba.SetRGBA(x, y, color.RGBA{0, 0, 0, 0}) } } } } // calculateAngles 计算旋转角度 func (r *SkinRenderer) calculateAngles() { // 转换为弧度 alpha := r.vR * math.Pi / 180 omega := r.hR * math.Pi / 180 r.cosAlpha = math.Cos(alpha) r.sinAlpha = math.Sin(alpha) r.cosOmega = math.Cos(omega) r.sinOmega = math.Sin(omega) r.membersAngles = make(map[string]angleSet) // 躯干不旋转 r.membersAngles["torso"] = angleSet{ cosAlpha: 1, sinAlpha: 0, cosOmega: 1, sinOmega: 0, } // 头部旋转 omegaHead := r.hrh * math.Pi / 180 r.membersAngles["head"] = angleSet{ cosAlpha: 1, sinAlpha: 0, cosOmega: math.Cos(omegaHead), sinOmega: math.Sin(omegaHead), } r.membersAngles["helmet"] = r.membersAngles["head"] // 右臂旋转 alphaRightArm := r.vrra * math.Pi / 180 r.membersAngles["rightArm"] = angleSet{ cosAlpha: math.Cos(alphaRightArm), sinAlpha: math.Sin(alphaRightArm), cosOmega: 1, sinOmega: 0, } // 左臂旋转 alphaLeftArm := r.vrla * math.Pi / 180 r.membersAngles["leftArm"] = angleSet{ cosAlpha: math.Cos(alphaLeftArm), sinAlpha: math.Sin(alphaLeftArm), cosOmega: 1, sinOmega: 0, } // 右腿旋转 alphaRightLeg := r.vrrl * math.Pi / 180 r.membersAngles["rightLeg"] = angleSet{ cosAlpha: math.Cos(alphaRightLeg), sinAlpha: math.Sin(alphaRightLeg), cosOmega: 1, sinOmega: 0, } // 左腿旋转 alphaLeftLeg := r.vrll * math.Pi / 180 r.membersAngles["leftLeg"] = angleSet{ cosAlpha: math.Cos(alphaLeftLeg), sinAlpha: math.Sin(alphaLeftLeg), cosOmega: 1, sinOmega: 0, } r.minX, r.maxX = 0, 0 r.minY, r.maxY = 0, 0 } // facesDetermination 确定可见面 func (r *SkinRenderer) facesDetermination() { r.visibleFaces = make(map[string]faceVisibility) parts := []string{"head", "torso", "rightArm", "leftArm", "rightLeg", "leftLeg"} for _, part := range parts { angles := r.membersAngles[part] // 创建测试立方体点 cubePoints := r.createCubePoints() var maxDepthPoint *Point var maxDepthFaces []string for _, cp := range cubePoints { point := cp.point point.PreProject(0, 0, 0, angles.cosAlpha, angles.sinAlpha, angles.cosOmega, angles.sinOmega) point.Project(r.cosAlpha, r.sinAlpha, r.cosOmega, r.sinOmega, &r.minX, &r.maxX, &r.minY, &r.maxY) if maxDepthPoint == nil { maxDepthPoint = point maxDepthFaces = cp.faces } else { _, _, z1 := maxDepthPoint.GetDestCoord() _, _, z2 := point.GetDestCoord() if z1 > z2 { maxDepthPoint = point maxDepthFaces = cp.faces } } } r.visibleFaces[part] = faceVisibility{ back: maxDepthFaces, front: diffFaces(allFaces, maxDepthFaces), } } // 确定全局前后面 cubePoints := r.createCubePoints() var maxDepthPoint *Point var maxDepthFaces []string for _, cp := range cubePoints { point := cp.point point.Project(r.cosAlpha, r.sinAlpha, r.cosOmega, r.sinOmega, &r.minX, &r.maxX, &r.minY, &r.maxY) if maxDepthPoint == nil { maxDepthPoint = point maxDepthFaces = cp.faces } else { _, _, z1 := maxDepthPoint.GetDestCoord() _, _, z2 := point.GetDestCoord() if z1 > z2 { maxDepthPoint = point maxDepthFaces = cp.faces } } } r.backFaces = maxDepthFaces r.frontFaces = diffFaces(allFaces, maxDepthFaces) } type cubePoint struct { point *Point faces []string } func (r *SkinRenderer) createCubePoints() []cubePoint { return []cubePoint{ {NewPoint(0, 0, 0), []string{"back", "right", "top"}}, {NewPoint(0, 0, 1), []string{"front", "right", "top"}}, {NewPoint(0, 1, 0), []string{"back", "right", "bottom"}}, {NewPoint(0, 1, 1), []string{"front", "right", "bottom"}}, {NewPoint(1, 0, 0), []string{"back", "left", "top"}}, {NewPoint(1, 0, 1), []string{"front", "left", "top"}}, {NewPoint(1, 1, 0), []string{"back", "left", "bottom"}}, {NewPoint(1, 1, 1), []string{"front", "left", "bottom"}}, } } func diffFaces(all, exclude []string) []string { excludeMap := make(map[string]bool) for _, f := range exclude { excludeMap[f] = true } var result []string for _, f := range all { if !excludeMap[f] { result = append(result, f) } } return result } func contains(slice []string, item string) bool { for _, s := range slice { if s == item { return true } } return false } // memberRotation 部件旋转 func (r *SkinRenderer) memberRotation() { hd := float64(r.hdRatio) // 头部和头盔旋转 angles := r.membersAngles["head"] for _, face := range r.polygons["head"] { for _, poly := range face { poly.PreProject(4*hd, 8*hd, 2*hd, angles.cosAlpha, angles.sinAlpha, angles.cosOmega, angles.sinOmega) } } for _, face := range r.polygons["helmet"] { for _, poly := range face { poly.PreProject(4*hd, 8*hd, 2*hd, angles.cosAlpha, angles.sinAlpha, angles.cosOmega, angles.sinOmega) } } if r.headOnly { return } // 右臂旋转 angles = r.membersAngles["rightArm"] for _, face := range r.polygons["rightArm"] { for _, poly := range face { poly.PreProject(-2*hd, 8*hd, 2*hd, angles.cosAlpha, angles.sinAlpha, angles.cosOmega, angles.sinOmega) } } // 左臂旋转 angles = r.membersAngles["leftArm"] for _, face := range r.polygons["leftArm"] { for _, poly := range face { poly.PreProject(10*hd, 8*hd, 2*hd, angles.cosAlpha, angles.sinAlpha, angles.cosOmega, angles.sinOmega) } } // 右腿旋转 angles = r.membersAngles["rightLeg"] zOffset := 4 * hd if angles.sinAlpha < 0 { zOffset = 0 } for _, face := range r.polygons["rightLeg"] { for _, poly := range face { poly.PreProject(2*hd, 20*hd, zOffset, angles.cosAlpha, angles.sinAlpha, angles.cosOmega, angles.sinOmega) } } // 左腿旋转 angles = r.membersAngles["leftLeg"] zOffset = 4 * hd if angles.sinAlpha < 0 { zOffset = 0 } for _, face := range r.polygons["leftLeg"] { for _, poly := range face { poly.PreProject(6*hd, 20*hd, zOffset, angles.cosAlpha, angles.sinAlpha, angles.cosOmega, angles.sinOmega) } } } // createProjectionPlan 创建投影 func (r *SkinRenderer) createProjectionPlan() { for _, piece := range r.polygons { for _, face := range piece { for _, poly := range face { if !poly.IsProjected() { poly.Project(r.cosAlpha, r.sinAlpha, r.cosOmega, r.sinOmega, &r.minX, &r.maxX, &r.minY, &r.maxY) } } } } } // displayImage 渲染最终图像 func (r *SkinRenderer) displayImage() image.Image { width := r.maxX - r.minX height := r.maxY - r.minY ratio := r.ratio * 2 srcWidth := int(ratio*width) + 1 srcHeight := int(ratio*height) + 1 img := image.NewRGBA(image.Rect(0, 0, srcWidth, srcHeight)) // 按深度顺序绘制 displayOrder := r.getDisplayOrder() for _, order := range displayOrder { for piece, faces := range order { for _, face := range faces { if polys, ok := r.polygons[piece][face]; ok { for _, poly := range polys { poly.AddToImage(img, r.minX, r.minY, ratio) } } } } } // 抗锯齿:2x 渲染后缩小 realWidth := srcWidth / 2 realHeight := srcHeight / 2 destImg := resizeImage(img, realWidth, realHeight) return destImg } // getDisplayOrder 获取绘制顺序 func (r *SkinRenderer) getDisplayOrder() []map[string][]string { var displayOrder []map[string][]string if contains(r.frontFaces, "top") { if contains(r.frontFaces, "right") { displayOrder = append(displayOrder, map[string][]string{"leftLeg": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"leftLeg": r.visibleFaces["leftLeg"].front}) displayOrder = append(displayOrder, map[string][]string{"rightLeg": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"rightLeg": r.visibleFaces["rightLeg"].front}) displayOrder = append(displayOrder, map[string][]string{"leftArm": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"leftArm": r.visibleFaces["leftArm"].front}) displayOrder = append(displayOrder, map[string][]string{"torso": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"torso": r.visibleFaces["torso"].front}) displayOrder = append(displayOrder, map[string][]string{"rightArm": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"rightArm": r.visibleFaces["rightArm"].front}) } else { displayOrder = append(displayOrder, map[string][]string{"rightLeg": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"rightLeg": r.visibleFaces["rightLeg"].front}) displayOrder = append(displayOrder, map[string][]string{"leftLeg": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"leftLeg": r.visibleFaces["leftLeg"].front}) displayOrder = append(displayOrder, map[string][]string{"rightArm": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"rightArm": r.visibleFaces["rightArm"].front}) displayOrder = append(displayOrder, map[string][]string{"torso": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"torso": r.visibleFaces["torso"].front}) displayOrder = append(displayOrder, map[string][]string{"leftArm": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"leftArm": r.visibleFaces["leftArm"].front}) } displayOrder = append(displayOrder, map[string][]string{"helmet": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"head": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"head": r.visibleFaces["head"].front}) displayOrder = append(displayOrder, map[string][]string{"helmet": r.visibleFaces["head"].front}) } else { displayOrder = append(displayOrder, map[string][]string{"helmet": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"head": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"head": r.visibleFaces["head"].front}) displayOrder = append(displayOrder, map[string][]string{"helmet": r.visibleFaces["head"].front}) if contains(r.frontFaces, "right") { displayOrder = append(displayOrder, map[string][]string{"leftArm": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"leftArm": r.visibleFaces["leftArm"].front}) displayOrder = append(displayOrder, map[string][]string{"torso": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"torso": r.visibleFaces["torso"].front}) displayOrder = append(displayOrder, map[string][]string{"rightArm": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"rightArm": r.visibleFaces["rightArm"].front}) displayOrder = append(displayOrder, map[string][]string{"leftLeg": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"leftLeg": r.visibleFaces["leftLeg"].front}) displayOrder = append(displayOrder, map[string][]string{"rightLeg": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"rightLeg": r.visibleFaces["rightLeg"].front}) } else { displayOrder = append(displayOrder, map[string][]string{"rightArm": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"rightArm": r.visibleFaces["rightArm"].front}) displayOrder = append(displayOrder, map[string][]string{"torso": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"torso": r.visibleFaces["torso"].front}) displayOrder = append(displayOrder, map[string][]string{"leftArm": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"leftArm": r.visibleFaces["leftArm"].front}) displayOrder = append(displayOrder, map[string][]string{"rightLeg": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"rightLeg": r.visibleFaces["rightLeg"].front}) displayOrder = append(displayOrder, map[string][]string{"leftLeg": r.backFaces}) displayOrder = append(displayOrder, map[string][]string{"leftLeg": r.visibleFaces["leftLeg"].front}) } } return displayOrder } // 辅助函数 func convertToRGBA(img image.Image) *image.RGBA { if rgba, ok := img.(*image.RGBA); ok { return rgba } bounds := img.Bounds() rgba := image.NewRGBA(bounds) for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { rgba.Set(x, y, img.At(x, y)) } } return rgba } func resizeImage(img image.Image, width, height int) *image.RGBA { bounds := img.Bounds() srcW := bounds.Dx() srcH := bounds.Dy() dst := image.NewRGBA(image.Rect(0, 0, width, height)) for y := 0; y < height; y++ { for x := 0; x < width; x++ { srcX := bounds.Min.X + x*srcW/width srcY := bounds.Min.Y + y*srcH/height dst.Set(x, y, img.At(srcX, srcY)) } } return dst } // getPixelColor 从皮肤图像获取像素颜色 func (r *SkinRenderer) getPixelColor(x, y int) color.RGBA { if x < 0 || y < 0 { return color.RGBA{} } bounds := r.playerSkin.Bounds() if x >= bounds.Dx() || y >= bounds.Dy() { return color.RGBA{} } c := r.playerSkin.At(bounds.Min.X+x, bounds.Min.Y+y) r32, g32, b32, a32 := c.RGBA() return color.RGBA{ R: uint8(r32 >> 8), G: uint8(g32 >> 8), B: uint8(b32 >> 8), A: uint8(a32 >> 8), } }