wxPython和pycairo练习记录16

不知道方向就先走着,最近又无所事事,准备照着别人的代码写一下 Mario 游戏,参照链接:https://github.com/mx0c/super-mario-python

虽然这位作者有写是受另一个项目启发,但是有些地方比如依赖关系也挺混乱的,但好在项目很完整,是用 pygame 写的,照着写总比自己瞎折腾容易吧。

先找出没有依赖其他文件的代码,可以作为后续其他代码的零件,这里选择 https://github.com/mx0c/super-mario-python/blob/master/classes/Spritesheet.py

文件结构直接按参考项目创建,主要用 cairo.ImageSurface 对象替代 pygame.image 对象,并另外实现 set_colorkey 指定颜色作为透明色。笔记和想法都直接写到注释里了。

# SpriteSheet.py
from cairo import ImageSurface, Context, Rectangle, FORMAT_ARGB32
from mario.classes.Utils import Utils
"""
cairo.ImageSurface 提供了一个灵活和强大的方式来在内存中创建和操作图像

在 Cairo 中,一切都被视为绘图操作,即使是对图像的修改也被视为在绘图上下文中进行的操作。
创建图像表面(Surface)的主要目的是为了提供一个统一的接口,使得对不同类型的绘图目标(比如窗口、PDF 文档、图像文件等)进行绘制操作时,能够使用相同的 API 和模式。

总之,将图像存储在 Surface 中的主要目的是为了提供一个统一的、灵活的、可移植的绘图接口,使得开发者可以更容易地进行绘图操作,并且可以轻松地将相同的绘图操作应用于不同的输出目标。
"""


class SpriteSheet:
    def __init__(self, filename):
        try:
            # 将 PNG 格式的精灵序列图片转换为 Surface
            self.sprite_sheet = ImageSurface.create_from_png(filename)
        except Exception as e:
            print(str(e))
            raise SystemExit

    def image_at(self, x, y, scale=1, color_key=None, is_column_row=True, tile_width=16, tile_height=16):
        if is_column_row:
            # x, y 作为行和列,取精灵图范围
            rect = Rectangle(x * tile_width, y * tile_height, tile_width, tile_height)
        else:
            # x, y 作为坐标,取精灵图范围
            rect = Rectangle(x, y, tile_width, tile_height)

        # 从精灵序列图中提取指定区域并创建为新的 Surface,surface.get_width() 结果为 0
        # surface = self.sprite_sheet.create_for_rectangle(*rect)
        # 创建缩放尺寸的空 Surface
        surface = ImageSurface(FORMAT_ARGB32, int(tile_width * scale), int(tile_height * scale))
        # 创建与指定 Surface 相关联的 Cairo 绘图上下文,这样就可以在指定的 Surface 上进行绘制操作。
        ctx = Context(surface)
        # 缩放 surface。缩放操作仅对后续绘制的几何图形进行缩放变换,而不会立即对图形上下文中的路径或 Surface 进行填充或绘制。
        ctx.scale(scale, scale)

        # self.sprite_sheet 为源 Surface,源 Surface 的坐标参数是指定其在目标 Surface 上的绘制位置,即源左上角相对于目标左上角作为原点的坐标。
        ctx.set_source_surface(self.sprite_sheet, -rect.x, -rect.y)
        # 应用所有设置样式进行填充
        ctx.paint()

        if color_key:
            if color_key == (-1, -1, -1):
                # 取 surface 像素数据
                data = surface.get_data()
                # 取 surface 第一个点 rgb 像素值
                color_key = tuple(data[:3])
            else:
                # surface data 实际排列为 bgra
                color_key = color_key[::-1]
            # 将 color_key 设为透明
            surface = Utils.set_colorkey(surface, color_key)

        return surface

# Utils.py
from cairo import ImageSurface, FORMAT_ARGB32


class Utils:
    @staticmethod
    def set_colorkey(surface, color_key):
        # 获取 Surface 的像素数据
        data = surface.get_data()

        # 获取 Surface 的宽度和高度
        width = surface.get_width()
        height = surface.get_height()

        # 遍历像素数据
        for y in range(height):
            for x in range(width):
                # 获取当前像素的索引
                index = y * width * 4 + x * 4
                # 获取当前像素的颜色
                pixel_color = (data[index], data[index + 1], data[index + 2])
                # 如果当前像素的颜色与目标颜色相同,则将 ALPHA 值设为 0
                if pixel_color == color_key:
                    data[index + 3] = 0

        # 将修改透明值的 data 转为 ImageSurface 对象
        return ImageSurface.create_for_data(data, FORMAT_ARGB32, width, height)

# test_SpriteSheet.py
import unittest
from mario.classes.SpriteSheet import SpriteSheet
# 仅包括功能测试,不包括异常测试


class TestSpriteSheet(unittest.TestCase):

    def test_spritesheet_init(self):
        ss = SpriteSheet("./img/tiles.png")
        self.assertEquals(ss.sprite_sheet.get_width(), 528)

    def test_spritesheet_image_at_is_column_row(self):
        ss = SpriteSheet("./img/tiles.png")
        surface = ss.image_at(0, 3)
        # surface.write_to_png("./test/test_SpriteSheet.png")
        self.assertEquals(surface.get_width(), 16)

    def test_spritesheet_image_at_not_column_row(self):
        ss = SpriteSheet("./img/tiles.png")
        surface = ss.image_at(17, 0, is_column_row=False)
        # surface.write_to_png("./test/test_SpriteSheet2.png")
        self.assertEquals(surface.get_width(), 16)

    def test_spritesheet_image_at_scale_zoom_in(self):
        ss = SpriteSheet("./img/tiles.png")
        surface = ss.image_at(0, 0, scale=10)
        # surface.write_to_png("./test/test_SpriteSheet3.png")
        self.assertEquals(surface.get_width(), 160)

    def test_spritesheet_image_at_scale_zoom_out(self):
        ss = SpriteSheet("./img/tiles.png")
        surface = ss.image_at(0, 0, scale=0.5)
        # surface.write_to_png("./test/test_SpriteSheet4.png")
        self.assertEquals(surface.get_width(), 8)

    def test_spritesheet_image_at_color_key_default(self):
        ss = SpriteSheet("./img/tiles.png")
        surface = ss.image_at(0, 0, color_key=(-1, -1, -1))
        # surface.write_to_png("./test/test_SpriteSheet5.png")
        self.assertEquals(tuple(surface.get_data()[:4]), (0, 74, 156, 0))

    def test_spritesheet_image_at_color_key_custom(self):
        ss = SpriteSheet("./img/tiles.png")
        surface = ss.image_at(0, 0, color_key=(255, 206, 198))
        # surface.write_to_png("./test/test_SpriteSheet6.png")
        self.assertEquals(tuple(surface.get_data()[4:8]), (198, 206, 255, 0))


if __name__ == '__main__':
    unittest.main(verbosity=2)

单元测试

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值