imagedd/model/sprite.go

209 lines
5.0 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 model
import (
"image"
"math"
)
type Sprite struct {
Name string
Position image.Point
Image image.Image
Index int
}
func (s *Sprite) SetImage(img *image.RGBA) {
s.Image = img
}
func (s *Sprite) Rotate(angle float64) {
// 创建旋转矩阵
sin, cos := math.Sin(angle), math.Cos(angle)
// 获取原始图像的边界
bounds := s.Image.Bounds()
width, height := bounds.Dx(), bounds.Dy()
centerX, centerY := width/2, height/2
// 计算旋转后图像的边界
corners := [4][2]int{
{0 - centerX, 0 - centerY},
{width - centerX, 0 - centerY},
{0 - centerX, height - centerY},
{width - centerX, height - centerY},
}
// 计算旋转后的四个角点位置
minX, minY, maxX, maxY := 0, 0, 0, 0
for i, p := range corners {
// 应用旋转
rotX := int(cos*float64(p[0]) - sin*float64(p[1]))
rotY := int(sin*float64(p[0]) + cos*float64(p[1]))
// 更新边界
if i == 0 || rotX < minX {
minX = rotX
}
if i == 0 || rotY < minY {
minY = rotY
}
if i == 0 || rotX > maxX {
maxX = rotX
}
if i == 0 || rotY > maxY {
maxY = rotY
}
}
// 创建新图像
newWidth, newHeight := maxX-minX+1, maxY-minY+1
newImage := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
newCenterX, newCenterY := newWidth/2, newHeight/2
// 预计算矩阵常量
invSin, invCos := math.Sin(-angle), math.Cos(-angle)
// 使用查找表存储sin/cos值避免重复计算
srcXOrigin := make([]int, newWidth)
srcYOrigin := make([]int, newHeight)
// 预计算X坐标的变换
for x := 0; x < newWidth; x++ {
srcXOrigin[x] = centerX
}
// 预计算Y坐标的变换
for y := 0; y < newHeight; y++ {
srcYOrigin[y] = centerY
}
// 批量处理像素
for y := 0; y < newHeight; y++ {
srcY := y - newCenterY
for x := 0; x < newWidth; x++ {
srcX := x - newCenterX
// 应用逆向旋转矩阵
origX := int(invCos*float64(srcX)-invSin*float64(srcY)) + centerX
origY := int(invSin*float64(srcX)+invCos*float64(srcY)) + centerY
// 检查是否在原图范围内
if origX >= 0 && origX < width && origY >= 0 && origY < height {
newImage.Set(x, y, s.Image.At(origX, origY))
}
}
}
s.Image = newImage
}
func (s *Sprite) Move(x, y int) {
s.Position.X += x
s.Position.Y += y
}
func (s *Sprite) Project(projectMatrix *ProjectMatrix) {
bounds := s.Image.Bounds()
// 计算四个角点投影后的位置
minX, minY, maxX, maxY := 0, 0, 0, 0
// 预先计算所有角点的投影,一次性确定边界
corners := [4]image.Point{
{bounds.Min.X, bounds.Min.Y},
{bounds.Max.X, bounds.Min.Y},
{bounds.Min.X, bounds.Max.Y},
{bounds.Max.X, bounds.Max.Y},
}
for i, corner := range corners {
projected := projectMatrix.ProjectPoint(corner)
if i == 0 {
minX, minY = projected.X, projected.Y
maxX, maxY = projected.X, projected.Y
} else {
if projected.X < minX {
minX = projected.X
}
if projected.Y < minY {
minY = projected.Y
}
if projected.X > maxX {
maxX = projected.X
}
if projected.Y > maxY {
maxY = projected.Y
}
}
}
// 创建新图像
newWidth, newHeight := maxX-minX+1, maxY-minY+1
newImage := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
// 计算逆矩阵
det := projectMatrix.Matrix[0][0]*projectMatrix.Matrix[1][1] -
projectMatrix.Matrix[0][1]*projectMatrix.Matrix[1][0]
if math.Abs(det) < 1e-6 {
// 矩阵接近不可逆
return
}
invDet := 1.0 / det
invMatrix := [2][2]float64{
{projectMatrix.Matrix[1][1] * invDet, -projectMatrix.Matrix[0][1] * invDet},
{-projectMatrix.Matrix[1][0] * invDet, projectMatrix.Matrix[0][0] * invDet},
}
// 预计算变换常量
m00, m01 := invMatrix[0][0], invMatrix[0][1]
m10, m11 := invMatrix[1][0], invMatrix[1][1]
// 使用直接内存访问优化像素设置
dstRGBA := newImage.Pix
srcImg, srcRGBA, _ := getImageBytes(s.Image)
// 批量处理像素
for y := 0; y < newHeight; y++ {
srcY := float64(y + minY)
for x := 0; x < newWidth; x++ {
srcX := float64(x + minX)
// 使用逆矩阵计算
origX := int(m00*srcX + m01*srcY)
origY := int(m10*srcX + m11*srcY)
// 检查边界
if origX >= bounds.Min.X && origX < bounds.Max.X &&
origY >= bounds.Min.Y && origY < bounds.Max.Y {
// 设置像素 - 直接使用indexing会更快
if srcRGBA != nil {
// 如果源图像是RGBA格式直接复制像素
dstIdx := (y*newImage.Stride + x*4)
srcIdx := ((origY-bounds.Min.Y)*srcImg.Stride + (origX-bounds.Min.X)*4)
dstRGBA[dstIdx] = srcRGBA[srcIdx] // R
dstRGBA[dstIdx+1] = srcRGBA[srcIdx+1] // G
dstRGBA[dstIdx+2] = srcRGBA[srcIdx+2] // B
dstRGBA[dstIdx+3] = srcRGBA[srcIdx+3] // A
} else {
// 否则使用通用方法
newImage.Set(x, y, s.Image.At(origX, origY))
}
}
}
}
s.Image = newImage
}
// getImageBytes 尝试提取图像的原始字节数据以加速访问
func getImageBytes(img image.Image) (*image.RGBA, []uint8, bool) {
if rgba, ok := img.(*image.RGBA); ok {
return rgba, rgba.Pix, true
}
return nil, nil, false
}