refactor: 移除旧的绘制模块,添加新的绘制辅助函数和精灵管理功能,增强图形处理能力。
This commit is contained in:
28
draw/draw.go
28
draw/draw.go
@@ -1,28 +0,0 @@
|
||||
package draw
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/draw"
|
||||
)
|
||||
|
||||
type Drawer interface {
|
||||
Draw(img *image.Image)
|
||||
}
|
||||
|
||||
type BaseBoard struct {
|
||||
Image *image.RGBA64
|
||||
}
|
||||
|
||||
func (b *BaseBoard) SetBaseImage(img *image.Image) {
|
||||
b.Image = ToRGBA64(img)
|
||||
}
|
||||
|
||||
func (b *BaseBoard) Draw(img *image.Image, pos image.Point) {
|
||||
draw.Draw(b.Image, b.Image.Bounds(), *img, pos, draw.Over)
|
||||
}
|
||||
|
||||
func ToRGBA64(img *image.Image) *image.RGBA64 {
|
||||
rgba := image.NewRGBA64((*img).Bounds())
|
||||
draw.Draw(rgba, rgba.Bounds(), *img, image.Point{}, draw.Over)
|
||||
return rgba
|
||||
}
|
||||
156
drawhelper/draw.go
Normal file
156
drawhelper/draw.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package drawhelper
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"log"
|
||||
|
||||
"git.lxtend.com/lixiangwuxian/imagedd/util"
|
||||
)
|
||||
|
||||
// 辅助函数:在图像上绘制宽线条
|
||||
func DrawThickLineOnImage(img *image.RGBA, x0, y0, x1, y1, width int, color color.Color) {
|
||||
// 获取颜色分量
|
||||
r, g, b, a := color.RGBA()
|
||||
r8, g8, b8, a8 := uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8)
|
||||
|
||||
bounds := img.Bounds()
|
||||
|
||||
// 使用Bresenham算法绘制线段作为中心线
|
||||
dx := util.Abs(x1 - x0)
|
||||
dy := util.Abs(y1 - y0)
|
||||
sx, sy := 1, 1
|
||||
if x0 >= x1 {
|
||||
sx = -1
|
||||
}
|
||||
if y0 >= y1 {
|
||||
sy = -1
|
||||
}
|
||||
err := dx - dy
|
||||
|
||||
// 计算线宽的一半(向下取整)
|
||||
halfWidth := width / 2
|
||||
|
||||
// 绘制线条
|
||||
x, y := x0, y0
|
||||
for {
|
||||
// 绘制宽线条(在中心点周围绘制圆形区域)
|
||||
for wy := -halfWidth; wy <= halfWidth; wy++ {
|
||||
for wx := -halfWidth; wx <= halfWidth; wx++ {
|
||||
// 只绘制在线宽范围内的点(圆形区域)
|
||||
if wx*wx+wy*wy <= halfWidth*halfWidth {
|
||||
DrawPointIfInBounds(img, bounds, x+wx, y+wy, r8, g8, b8, a8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if x == x1 && y == y1 {
|
||||
break
|
||||
}
|
||||
|
||||
e2 := 2 * err
|
||||
if e2 > -dy {
|
||||
err -= dy
|
||||
x += sx
|
||||
}
|
||||
if e2 < dx {
|
||||
err += dx
|
||||
y += sy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:在图像上绘制线段
|
||||
func DrawLineOnImage(img *image.RGBA, x0, y0, x1, y1 int, color color.Color) {
|
||||
r, g, b, a := color.RGBA()
|
||||
r8, g8, b8, a8 := uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8)
|
||||
|
||||
// 输出颜色值用于调试
|
||||
log.Printf("绘制线条: 颜色 RGBA(%d,%d,%d,%d)", r8, g8, b8, a8)
|
||||
|
||||
bounds := img.Bounds()
|
||||
|
||||
// 使用Bresenham算法绘制线段
|
||||
dx := util.Abs(x1 - x0)
|
||||
dy := util.Abs(y1 - y0)
|
||||
sx, sy := 1, 1
|
||||
if x0 >= x1 {
|
||||
sx = -1
|
||||
}
|
||||
if y0 >= y1 {
|
||||
sy = -1
|
||||
}
|
||||
err := dx - dy
|
||||
|
||||
for {
|
||||
// 绘制点,但只在有效范围内
|
||||
DrawPointIfInBounds(img, bounds, x0, y0, r8, g8, b8, a8)
|
||||
|
||||
if x0 == x1 && y0 == y1 {
|
||||
break
|
||||
}
|
||||
|
||||
e2 := 2 * err
|
||||
if e2 > -dy {
|
||||
err -= dy
|
||||
x0 += sx
|
||||
}
|
||||
if e2 < dx {
|
||||
err += dx
|
||||
y0 += sy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:在图像上绘制圆
|
||||
func DrawCircleOnImage(img *image.RGBA, x0, y0, radius int, color color.Color) {
|
||||
r, g, b, a := color.RGBA()
|
||||
r8, g8, b8, a8 := uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8)
|
||||
|
||||
// 使用Bresenham算法绘制圆
|
||||
x := 0
|
||||
y := radius
|
||||
d := 3 - 2*radius
|
||||
|
||||
bounds := img.Bounds()
|
||||
|
||||
for {
|
||||
// 绘制8个对称点,但只在图像范围内
|
||||
DrawPointIfInBounds(img, bounds, x0+x, y0+y, r8, g8, b8, a8)
|
||||
DrawPointIfInBounds(img, bounds, x0+x, y0-y, r8, g8, b8, a8)
|
||||
DrawPointIfInBounds(img, bounds, x0-x, y0+y, r8, g8, b8, a8)
|
||||
DrawPointIfInBounds(img, bounds, x0-x, y0-y, r8, g8, b8, a8)
|
||||
DrawPointIfInBounds(img, bounds, x0+y, y0+x, r8, g8, b8, a8)
|
||||
DrawPointIfInBounds(img, bounds, x0+y, y0-x, r8, g8, b8, a8)
|
||||
DrawPointIfInBounds(img, bounds, x0-y, y0+x, r8, g8, b8, a8)
|
||||
DrawPointIfInBounds(img, bounds, x0-y, y0-x, r8, g8, b8, a8)
|
||||
|
||||
if d < 0 {
|
||||
d += 4*x + 6
|
||||
} else {
|
||||
d += 4*(x-y) + 10
|
||||
y--
|
||||
}
|
||||
x++
|
||||
|
||||
if x > y {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:在图像上设置像素,但只在有效范围内
|
||||
func DrawPointIfInBounds(img *image.RGBA, bounds image.Rectangle, x, y int, r, g, b, a uint8) {
|
||||
if x >= bounds.Min.X && x < bounds.Max.X && y >= bounds.Min.Y && y < bounds.Max.Y {
|
||||
// 记录绘制的像素位置和颜色,便于调试
|
||||
if (r > 0 || g > 0 || b > 0) && a > 0 {
|
||||
log.Printf("绘制像素: (%d,%d) RGBA(%d,%d,%d,%d)", x, y, r, g, b, a)
|
||||
}
|
||||
|
||||
idx := (y-bounds.Min.Y)*img.Stride + (x-bounds.Min.X)*4
|
||||
img.Pix[idx] = r
|
||||
img.Pix[idx+1] = g
|
||||
img.Pix[idx+2] = b
|
||||
img.Pix[idx+3] = a
|
||||
}
|
||||
}
|
||||
41
model/2d.go
41
model/2d.go
@@ -1,41 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
)
|
||||
|
||||
type Line struct {
|
||||
Start image.Point
|
||||
End image.Point
|
||||
Width int
|
||||
Color color.Color
|
||||
}
|
||||
|
||||
func (l *Line) ToSprite() *Sprite {
|
||||
img := image.NewRGBA(image.Rect(0, 0, l.End.X-l.Start.X, l.End.Y-l.Start.Y))
|
||||
draw.Draw(img, image.Rect(0, 0, l.End.X-l.Start.X, l.End.Y-l.Start.Y), &image.Uniform{l.Color}, image.Point{}, draw.Src)
|
||||
return &Sprite{
|
||||
Position: l.Start,
|
||||
Image: img,
|
||||
}
|
||||
}
|
||||
|
||||
type ProjectMatrix struct {
|
||||
Matrix [2][2]float64
|
||||
}
|
||||
|
||||
func (p *ProjectMatrix) ProjectPoint(point image.Point) image.Point {
|
||||
return image.Point{
|
||||
X: int(p.Matrix[0][0]*float64(point.X) + p.Matrix[0][1]*float64(point.Y)),
|
||||
Y: int(p.Matrix[1][0]*float64(point.X) + p.Matrix[1][1]*float64(point.Y)),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProjectMatrix) ProjectLine(line Line) Line {
|
||||
return Line{
|
||||
Start: p.ProjectPoint(line.Start),
|
||||
End: p.ProjectPoint(line.End),
|
||||
}
|
||||
}
|
||||
134
sprite/2d.go
Normal file
134
sprite/2d.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package sprite
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"log"
|
||||
|
||||
"git.lxtend.com/lixiangwuxian/imagedd/drawhelper"
|
||||
)
|
||||
|
||||
type Line struct {
|
||||
Start image.Point
|
||||
End image.Point
|
||||
Width int
|
||||
Color color.Color
|
||||
}
|
||||
|
||||
// AddToSprite 在精灵图上绘制线条,保留原有的图像
|
||||
func (l *Line) AddToSprite(sprite *Sprite) {
|
||||
log.Printf("开始绘制线条: 起点(%d,%d) 终点(%d,%d) 宽度%d",
|
||||
l.Start.X, l.Start.Y, l.End.X, l.End.Y, l.Width)
|
||||
|
||||
// 检查精灵是否已有图像
|
||||
if sprite.Image == nil {
|
||||
// 如果没有图像,创建一个新图像
|
||||
width := l.End.X - l.Start.X + 1
|
||||
height := l.End.Y - l.Start.Y + 1
|
||||
if width < 1 {
|
||||
width = 1
|
||||
}
|
||||
if height < 1 {
|
||||
height = 1
|
||||
}
|
||||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
sprite.Image = img
|
||||
sprite.Position = l.Start
|
||||
log.Printf("创建新图像: 大小(%d,%d) 位置(%d,%d)",
|
||||
width, height, sprite.Position.X, sprite.Position.Y)
|
||||
}
|
||||
|
||||
// 获取当前图像大小和位置
|
||||
bounds := sprite.Image.Bounds()
|
||||
width, height := bounds.Dx(), bounds.Dy()
|
||||
log.Printf("当前图像: 大小(%d,%d) 位置(%d,%d)",
|
||||
width, height, sprite.Position.X, sprite.Position.Y)
|
||||
|
||||
// 直接使用原图像,不再扩展
|
||||
var newImage *image.RGBA
|
||||
newImage, _ = sprite.Image.(*image.RGBA)
|
||||
if newImage == nil {
|
||||
// 如果原图像不是RGBA,转换为RGBA
|
||||
newImage = image.NewRGBA(bounds)
|
||||
draw.Draw(newImage, bounds, sprite.Image, bounds.Min, draw.Src)
|
||||
log.Printf("转换图像为RGBA格式")
|
||||
}
|
||||
|
||||
// 在图像上绘制线条,根据线宽决定使用哪个函数
|
||||
// 超出图像边界的部分将被自动丢弃AddToSprite
|
||||
if l.Width <= 1 {
|
||||
// 使用标准线条绘制算法
|
||||
drawhelper.DrawLineOnImage(newImage, l.Start.X, l.Start.Y, l.End.X, l.End.Y, l.Color)
|
||||
} else {
|
||||
// 使用粗线条绘制算法
|
||||
drawhelper.DrawThickLineOnImage(newImage, l.Start.X, l.Start.Y, l.End.X, l.End.Y, l.Width, l.Color)
|
||||
}
|
||||
|
||||
// 更新精灵图像
|
||||
sprite.Image = newImage
|
||||
log.Printf("线条绘制完成")
|
||||
}
|
||||
|
||||
type Circle struct {
|
||||
Center image.Point
|
||||
Radius int
|
||||
Color color.Color
|
||||
}
|
||||
|
||||
// ToSprite 在精灵图上绘制圆形,保留原有的图像
|
||||
func (c *Circle) ToSprite(sprite *Sprite) {
|
||||
// 检查精灵是否已有图像
|
||||
if sprite.Image == nil {
|
||||
// 如果没有图像,创建一个新图像
|
||||
size := c.Radius * 2
|
||||
img := image.NewRGBA(image.Rect(0, 0, size, size))
|
||||
sprite.Image = img
|
||||
// 设置精灵位置为圆心减去半径
|
||||
sprite.Position = image.Point{
|
||||
X: c.Center.X - c.Radius,
|
||||
Y: c.Center.Y - c.Radius,
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前图像大小和位置
|
||||
bounds := sprite.Image.Bounds()
|
||||
|
||||
// 计算圆心相对于精灵图像的位置
|
||||
relCenterX := c.Center.X - sprite.Position.X
|
||||
relCenterY := c.Center.Y - sprite.Position.Y
|
||||
|
||||
// 直接使用原图像,不再扩展
|
||||
var newImage *image.RGBA
|
||||
newImage, _ = sprite.Image.(*image.RGBA)
|
||||
if newImage == nil {
|
||||
// 如果原图像不是RGBA,转换为RGBA
|
||||
newImage = image.NewRGBA(bounds)
|
||||
draw.Draw(newImage, bounds, sprite.Image, bounds.Min, draw.Src)
|
||||
}
|
||||
|
||||
// 在新图像上绘制圆
|
||||
// 超出图像边界的部分将被自动丢弃
|
||||
drawhelper.DrawCircleOnImage(newImage, relCenterX, relCenterY, c.Radius, c.Color)
|
||||
|
||||
// 更新精灵图像
|
||||
sprite.Image = newImage
|
||||
}
|
||||
|
||||
type ProjectMatrix struct {
|
||||
Matrix [2][2]float64
|
||||
}
|
||||
|
||||
func (p *ProjectMatrix) ProjectPoint(point image.Point) image.Point {
|
||||
return image.Point{
|
||||
X: int(p.Matrix[0][0]*float64(point.X) + p.Matrix[0][1]*float64(point.Y)),
|
||||
Y: int(p.Matrix[1][0]*float64(point.X) + p.Matrix[1][1]*float64(point.Y)),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProjectMatrix) ProjectLine(line Line) Line {
|
||||
return Line{
|
||||
Start: p.ProjectPoint(line.Start),
|
||||
End: p.ProjectPoint(line.End),
|
||||
}
|
||||
}
|
||||
284
sprite/2d_test.go
Normal file
284
sprite/2d_test.go
Normal file
@@ -0,0 +1,284 @@
|
||||
package sprite
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 测试在精灵上绘制线条
|
||||
func TestLineToSprite(t *testing.T) {
|
||||
// 创建一个新的空白精灵
|
||||
sprite := &Sprite{
|
||||
Name: "test_line",
|
||||
Position: image.Point{X: 10, Y: 10},
|
||||
Index: 1,
|
||||
}
|
||||
|
||||
// 定义一条线
|
||||
line := &Line{
|
||||
Start: image.Point{X: 10, Y: 10}, // 起点设置为与精灵位置相同
|
||||
End: image.Point{X: 50, Y: 30},
|
||||
Width: 5, // 增加线宽以便更容易找到
|
||||
Color: color.RGBA{R: 255, G: 0, B: 0, A: 255}, // 红色
|
||||
}
|
||||
|
||||
// 在精灵上绘制线
|
||||
line.AddToSprite(sprite)
|
||||
|
||||
// 验证精灵现在有了图像
|
||||
if sprite.Image == nil {
|
||||
t.Fatalf("精灵图像为空")
|
||||
}
|
||||
|
||||
// 验证精灵位置未变(因为线条在精灵边界内)
|
||||
if sprite.Position.X != 10 || sprite.Position.Y != 10 {
|
||||
t.Errorf("精灵位置错误:期望(10,10),实际(%d,%d)",
|
||||
sprite.Position.X, sprite.Position.Y)
|
||||
}
|
||||
|
||||
// 检查图像大小
|
||||
bounds := sprite.Image.Bounds()
|
||||
t.Logf("图像大小:%dx%d", bounds.Dx(), bounds.Dy())
|
||||
if bounds.Dx() < 41 || bounds.Dy() < 21 {
|
||||
t.Errorf("图像大小错误:期望至少(41x21),实际(%dx%d)",
|
||||
bounds.Dx(), bounds.Dy())
|
||||
}
|
||||
|
||||
// 检查图像中是否有红色像素
|
||||
redPixelFound := false
|
||||
|
||||
// 扫描整个图像查找红色像素
|
||||
var redPixels []image.Point
|
||||
for y := 0; y < bounds.Dy(); y++ {
|
||||
for x := 0; x < bounds.Dx(); x++ {
|
||||
pixelColor := sprite.Image.At(x, y)
|
||||
r, g, b, _ := pixelColor.RGBA()
|
||||
if r>>8 > 200 && g>>8 < 50 && b>>8 < 50 {
|
||||
redPixelFound = true
|
||||
redPixels = append(redPixels, image.Point{X: x, Y: y})
|
||||
if len(redPixels) >= 5 { // 找到5个点就足够了
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(redPixels) >= 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 打印找到的红色像素位置
|
||||
if len(redPixels) > 0 {
|
||||
t.Logf("找到 %d 个红色像素点", len(redPixels))
|
||||
for i, p := range redPixels {
|
||||
t.Logf(" 红色像素点 #%d: (%d,%d)", i+1, p.X, p.Y)
|
||||
}
|
||||
}
|
||||
|
||||
if !redPixelFound {
|
||||
// 如果没找到,保存图像用于调试
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_line1.png")
|
||||
t.Errorf("未找到红色线条像素")
|
||||
}
|
||||
|
||||
// 保存初始图像大小用于后续比较
|
||||
initialWidth := bounds.Dx()
|
||||
initialHeight := bounds.Dy()
|
||||
|
||||
// 再绘制一条线(线条一部分超出当前图像边界,应导致图像扩展)
|
||||
line2 := &Line{
|
||||
Start: image.Point{X: 5, Y: 20},
|
||||
End: image.Point{X: 65, Y: 40},
|
||||
Width: 5, // 增加线宽
|
||||
Color: color.RGBA{R: 0, G: 0, B: 255, A: 255}, // 蓝色
|
||||
}
|
||||
|
||||
// 使用Sprite.DrawLine方法
|
||||
sprite.DrawLine(line2)
|
||||
|
||||
// 检查图像是否扩展以容纳新线
|
||||
newBounds := sprite.Image.Bounds()
|
||||
t.Logf("扩展后图像大小:%dx%d", newBounds.Dx(), newBounds.Dy())
|
||||
if newBounds.Dx() <= initialWidth || newBounds.Dy() <= initialHeight {
|
||||
t.Errorf("图像未扩展:原始(%dx%d),现在(%dx%d)",
|
||||
initialWidth, initialHeight, newBounds.Dx(), newBounds.Dy())
|
||||
}
|
||||
|
||||
// 检查图像中是否有蓝色像素
|
||||
bluePixelFound := false
|
||||
|
||||
// 扫描整个图像查找蓝色像素
|
||||
var bluePixels []image.Point
|
||||
for y := 0; y < newBounds.Dy(); y++ {
|
||||
for x := 0; x < newBounds.Dx(); x++ {
|
||||
pixelColor := sprite.Image.At(x, y)
|
||||
r, g, b, _ := pixelColor.RGBA()
|
||||
if r>>8 < 50 && g>>8 < 50 && b>>8 > 200 {
|
||||
bluePixelFound = true
|
||||
bluePixels = append(bluePixels, image.Point{X: x, Y: y})
|
||||
if len(bluePixels) >= 5 { // 找到5个点就足够了
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(bluePixels) >= 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 打印找到的蓝色像素位置
|
||||
if len(bluePixels) > 0 {
|
||||
t.Logf("找到 %d 个蓝色像素点", len(bluePixels))
|
||||
for i, p := range bluePixels {
|
||||
t.Logf(" 蓝色像素点 #%d: (%d,%d)", i+1, p.X, p.Y)
|
||||
}
|
||||
}
|
||||
|
||||
if !bluePixelFound {
|
||||
// 保存图像用于调试
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_line2.png")
|
||||
t.Errorf("未找到蓝色线条像素")
|
||||
}
|
||||
|
||||
// 总是保存最终图像以便查看结果
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_line_final.png")
|
||||
}
|
||||
|
||||
// 测试在精灵上绘制圆形
|
||||
func TestCircleToSprite(t *testing.T) {
|
||||
// 创建一个新的空白精灵
|
||||
sprite := &Sprite{
|
||||
Name: "test_circle",
|
||||
Position: image.Point{X: 20, Y: 20},
|
||||
Index: 1,
|
||||
}
|
||||
|
||||
// 定义一个圆
|
||||
circle := &Circle{
|
||||
Center: image.Point{X: 30, Y: 30},
|
||||
Radius: 10,
|
||||
Color: color.RGBA{R: 0, G: 255, B: 0, A: 255}, // 绿色
|
||||
}
|
||||
|
||||
// 在精灵上绘制圆
|
||||
circle.ToSprite(sprite)
|
||||
|
||||
// 验证精灵现在有了图像
|
||||
if sprite.Image == nil {
|
||||
t.Fatalf("精灵图像为空")
|
||||
}
|
||||
|
||||
// 验证精灵位置正确更新
|
||||
if sprite.Position.X != 20 || sprite.Position.Y != 20 {
|
||||
t.Errorf("精灵位置错误:期望(20,20),实际(%d,%d)",
|
||||
sprite.Position.X, sprite.Position.Y)
|
||||
}
|
||||
|
||||
// 检查图像大小
|
||||
bounds := sprite.Image.Bounds()
|
||||
t.Logf("图像大小:%dx%d", bounds.Dx(), bounds.Dy())
|
||||
|
||||
// 圆的相对坐标是 (30-20, 30-20) 即 (10,10) 中心
|
||||
// 记录找到绿色像素的位置,用于调试
|
||||
foundPoints := make([]image.Point, 0)
|
||||
|
||||
// 在图像中寻找绿色像素
|
||||
for y := 0; y < bounds.Dy(); y++ {
|
||||
for x := 0; x < bounds.Dx(); x++ {
|
||||
pixelColor := sprite.Image.At(x, y)
|
||||
r, g, b, _ := pixelColor.RGBA()
|
||||
if r>>8 < 50 && g>>8 > 200 && b>>8 < 50 {
|
||||
foundPoints = append(foundPoints, image.Point{X: x, Y: y})
|
||||
if len(foundPoints) > 10 {
|
||||
break // 找到足够多的点了
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(foundPoints) > 10 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找到了绿色像素,则测试通过
|
||||
if len(foundPoints) == 0 {
|
||||
// 保存图像用于调试
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_circle1.png")
|
||||
t.Errorf("未找到绿色圆形像素")
|
||||
} else {
|
||||
t.Logf("找到 %d 个绿色像素点", len(foundPoints))
|
||||
for i, p := range foundPoints {
|
||||
if i < 5 { // 只打印前5个点
|
||||
t.Logf(" 绿色像素点 #%d: (%d,%d)", i+1, p.X, p.Y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存初始图像大小用于后续比较
|
||||
initialWidth := bounds.Dx()
|
||||
initialHeight := bounds.Dy()
|
||||
|
||||
// 再绘制一个圆(部分在图像边界外,应导致图像扩展)
|
||||
circle2 := &Circle{
|
||||
Center: image.Point{X: 15, Y: 15},
|
||||
Radius: 15,
|
||||
Color: color.RGBA{R: 255, G: 255, B: 0, A: 255}, // 黄色
|
||||
}
|
||||
|
||||
// 在已有精灵上绘制第二个圆
|
||||
circle2.ToSprite(sprite)
|
||||
|
||||
// 检查图像是否扩展
|
||||
newBounds := sprite.Image.Bounds()
|
||||
t.Logf("扩展后图像大小:%dx%d", newBounds.Dx(), newBounds.Dy())
|
||||
|
||||
// 检查图像是否扩展了
|
||||
if newBounds.Dx() <= initialWidth || newBounds.Dy() <= initialHeight {
|
||||
t.Errorf("图像未正确扩展:原始(%dx%d),现在(%dx%d)",
|
||||
initialWidth, initialHeight, newBounds.Dx(), newBounds.Dy())
|
||||
}
|
||||
|
||||
// 查找黄色像素
|
||||
yellowPixelFound := false
|
||||
yellowPoints := make([]image.Point, 0)
|
||||
|
||||
// 在图像中寻找黄色像素
|
||||
for y := 0; y < newBounds.Dy(); y++ {
|
||||
for x := 0; x < newBounds.Dx(); x++ {
|
||||
pixelColor := sprite.Image.At(x, y)
|
||||
r, g, b, _ := pixelColor.RGBA()
|
||||
if r>>8 > 200 && g>>8 > 200 && b>>8 < 50 {
|
||||
yellowPixelFound = true
|
||||
yellowPoints = append(yellowPoints, image.Point{X: x, Y: y})
|
||||
if len(yellowPoints) > 10 {
|
||||
break // 找到足够多的点了
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(yellowPoints) > 10 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !yellowPixelFound {
|
||||
// 保存图像用于调试
|
||||
board := NewNamedSpriteBoard(100, 100)
|
||||
board.AddSprite(sprite)
|
||||
board.SaveToPng("test_circle2.png")
|
||||
t.Errorf("未找到黄色圆形像素")
|
||||
} else {
|
||||
t.Logf("找到 %d 个黄色像素点", len(yellowPoints))
|
||||
for i, p := range yellowPoints {
|
||||
if i < 5 { // 只打印前5个点
|
||||
t.Logf(" 黄色像素点 #%d: (%d,%d)", i+1, p.X, p.Y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package model
|
||||
package sprite
|
||||
|
||||
import (
|
||||
"image"
|
||||
@@ -501,8 +501,8 @@ func (b *NamedSpriteBoard) RenderToImage() *image.RGBA {
|
||||
return img
|
||||
}
|
||||
|
||||
// SaveToFile 将精灵板渲染为图像并保存到文件
|
||||
func (b *NamedSpriteBoard) SaveToFile(filename string) error {
|
||||
// SaveToPng 将精灵板渲染为图像并保存到文件
|
||||
func (b *NamedSpriteBoard) SaveToPng(filename string) error {
|
||||
// 渲染图像
|
||||
img := b.RenderToImage()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package model
|
||||
package sprite
|
||||
|
||||
import (
|
||||
"image"
|
||||
@@ -333,7 +333,7 @@ func TestNamedSpriteBoardRenderToImage(t *testing.T) {
|
||||
// 将saveImage设置为true查看实际渲染效果以调试
|
||||
saveImage := false
|
||||
if saveImage {
|
||||
err := board.SaveToFile("test_render.png")
|
||||
err := board.SaveToPng("test_render.png")
|
||||
if err != nil {
|
||||
t.Errorf("保存图像失败: %v", err)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package model
|
||||
package sprite
|
||||
|
||||
import (
|
||||
"image"
|
||||
@@ -199,6 +199,11 @@ func (s *Sprite) Project(projectMatrix *ProjectMatrix) {
|
||||
s.Image = newImage
|
||||
}
|
||||
|
||||
func (s *Sprite) DrawLine(line *Line) {
|
||||
// 使用Line.ToSprite方法在当前精灵上绘制线条
|
||||
line.AddToSprite(s)
|
||||
}
|
||||
|
||||
// getImageBytes 尝试提取图像的原始字节数据以加速访问
|
||||
func getImageBytes(img image.Image) (*image.RGBA, []uint8, bool) {
|
||||
if rgba, ok := img.(*image.RGBA); ok {
|
||||
25
util/math.go
Normal file
25
util/math.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package util
|
||||
|
||||
// 辅助函数:绝对值
|
||||
func Abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// 辅助函数:最小值
|
||||
func Min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 辅助函数:最大值
|
||||
func Max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user