Files
backend/internal/service/skin_renderer/cape_renderer.go

122 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package skin_renderer
import (
"bytes"
"image"
"image/png"
)
// CapeRenderer 披风渲染器
type CapeRenderer struct{}
// NewCapeRenderer 创建披风渲染器
func NewCapeRenderer() *CapeRenderer {
return &CapeRenderer{}
}
// Render 渲染披风
// 披风纹理布局:
// - 正面: (1, 1) 到 (11, 17) - 10x16 像素
// - 背面: (12, 1) 到 (22, 17) - 10x16 像素
func (r *CapeRenderer) Render(capeData []byte, height int) (image.Image, error) {
// 解码披风图像
img, err := png.Decode(bytes.NewReader(capeData))
if err != nil {
return nil, err
}
bounds := img.Bounds()
srcWidth := bounds.Dx()
srcHeight := bounds.Dy()
// 披风纹理可能是 64x32 或 22x17
// 标准披风正面区域
var frontX, frontY, frontW, frontH int
if srcWidth >= 64 && srcHeight >= 32 {
// 64x32 格式Minecraft 1.8+
// 正面: (1, 1) 到 (11, 17)
frontX = 1
frontY = 1
frontW = 10
frontH = 16
} else if srcWidth >= 22 && srcHeight >= 17 {
// 22x17 格式(旧版)
frontX = 1
frontY = 1
frontW = 10
frontH = 16
} else {
// 未知格式,直接缩放整个图像
return resizeImageBilinear(img, height*srcWidth/srcHeight, height), nil
}
// 提取正面区域
front := image.NewRGBA(image.Rect(0, 0, frontW, frontH))
for y := 0; y < frontH; y++ {
for x := 0; x < frontW; x++ {
front.Set(x, y, img.At(bounds.Min.X+frontX+x, bounds.Min.Y+frontY+y))
}
}
// 计算输出尺寸,保持宽高比
outputWidth := height * frontW / frontH
if outputWidth < 1 {
outputWidth = 1
}
// 使用最近邻缩放保持像素风格
return scaleNearest(front, outputWidth, height), nil
}
// scaleNearest 最近邻缩放
func scaleNearest(src image.Image, width, height int) *image.RGBA {
bounds := src.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, src.At(srcX, srcY))
}
}
return dst
}
// resizeImageBilinear 双线性插值缩放
func resizeImageBilinear(src image.Image, width, height int) *image.RGBA {
bounds := src.Bounds()
srcW := float64(bounds.Dx())
srcH := float64(bounds.Dy())
dst := image.NewRGBA(image.Rect(0, 0, width, height))
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
// 计算源图像中的位置
srcX := float64(x) * srcW / float64(width)
srcY := float64(y) * srcH / float64(height)
// 简单的最近邻(可以改进为双线性)
ix := int(srcX)
iy := int(srcY)
if ix >= bounds.Dx() {
ix = bounds.Dx() - 1
}
if iy >= bounds.Dy() {
iy = bounds.Dy() - 1
}
dst.Set(x, y, src.At(bounds.Min.X+ix, bounds.Min.Y+iy))
}
}
return dst
}