暂存服务端渲染功能,材质渲染计划迁移至前端
This commit is contained in:
121
internal/service/skin_renderer/cape_renderer.go
Normal file
121
internal/service/skin_renderer/cape_renderer.go
Normal file
@@ -0,0 +1,121 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user