如何使用 Surface 表面对象?以及表面(图像)的透明度,透明颜色键,颜色,大小等问题
关注 1260
本节内容不涉及矩形的碰撞检测和子表面。
如何在 Pygame 中获取表面大小及其矩形对象视频演示 YouTube如何在 Pygame 中获取表面大小及其矩形对象视频演示 Bilibili
如何在 Pygame 中获取和设置表面的透明度视频演示 YouTube如何在 Pygame 中获取和设置表面的透明度视频演示 Bilibili
Pygame 中的 Surface 对象
在 Pygame 中,可以将Surface
表面对象理解为画布,一些内容会被绘制在画布中,比如,图像,图形或另一个画布。某些模块的函数的返回值是一个Surface
表面对象,比如,display
模块的set_mode
函数,如果你希望使用构造器创建Surface
对象,则size
参数是必需的,其他参数可以省略。一旦Surface
对象被创建,其特性和颜色深度将是不可改变的,并将被黑色填充。
Surface(size, flags, depth, masks)
Surface(size, flags, surface)
- size 参数
size
参数表示表面的大小,他可以是依次包含表面宽度和高度的 Python 浮点数序列对象,或一个Vector2
对象(对象的x
变量对应表面宽度,对象的y
变量对应表面高度)。- flags 参数
flags
参数是一个整数,决定了表面对象的一些特性,可以是以下pygame
模块变量,SRCALPHA
(使用颜色中的透明度信息来计算透明度)。- depth 参数
depth
参数是表示颜色深度的整数,一般不需要设置,除非你希望使用自己的像素格式,随意指定depth
可能导致游戏运行缓慢。- masks 参数
masks
参数是一个包含四个整数的 Python 元组,表示用于转换颜色和映射整数的掩码。- surface 参数
surface
参数是作为参考的另一个表面对象,当你没有或无法提供参数flags
,depth
或masks
时,将采用该表面对象的特性,颜色深度或掩码。
在下面的示例中,表面s1
和表面s2
的颜色深度相同。
# 导入 Surface 对象
from pygame import Surface
# 创建大小为 800x600,颜色深度为 16 的表面
s1 = Surface((800, 600), depth=16)
print(f'颜色深度:{s1.get_bitsize()}')
# 表面 s2 的颜色深度与 s1 相同
s2 = Surface([400, 300], 0, s1)
print(f'颜色深度:{s2.get_bitsize()}')
颜色深度:16
颜色深度:16
获取表面的大小
Surface
对象的get_width
,get_height
,get_size
方法,可用于获取表面的宽度,高度和大小。
get_width()
- 返回值
get_width
方法的返回值是表示表面宽度的整数。
get_height()
- 返回值
get_height
方法的返回值是表示表面高度的整数。
get_size()
- 返回值
get_size
方法的返回值是形式为(width,height)
的 Python 整数元组,其中width
为表面宽度,height
为表面高度。
from pygame import Surface
# 创建大小为 800x600 的表面
s = Surface((800, 600))
# 显示表面的大小
print(f'宽度:{s.get_width()} 高度:{s.get_height()} 大小:{s.get_size()}')
宽度:800 高度:600 大小:(800, 600)
根据表面的大小创建矩形
Surface
对象的get_rect
方法,可根据表面的大小创建一个新的矩形,默认情况下,新矩形的左上角为原点,大小与表面相同。
get_rect(**kwargs)
- kwargs 参数
可变参数
kwargs
所包含的位置参数将用于设置新的矩形对象,位置参数的名称需要与矩形对象的变量名称相同。- 返回值
get_rect
方法的返回值是Rect
对象。
矩形
关于Rect
矩形对象的变量,你可以查看获取和设置矩形信息一段。
在下面的示例中,我们根据表面对象s
创建了一个新的矩形,并设置了该矩形的左上角坐标。
from pygame import Surface
# 创建大小为 800x600 的表面
s = Surface((800, 600))
# 根据表面创建矩形,并设置左上角坐标为 10 10
print(s.get_rect(topleft=(10, 10)))
<rect(10, 10, 800, 600)>
获取表面的特性
Surface
对象的get_flags
方法,可用于获取表面的特性,你可以通过与位运算符&
来判断表面是否具有某一特性。以下是一些表示表面特性的pygame
模块变量,HWACCEL
(使用硬件加速),RLEACCEL
(表面采用 RLE 编码),SWSURFACE
(表面存储在系统内存,而非显卡内存),PREALLOC
(表面采用预分配内存),SRCALPHA
(表面支持像素颜色中的透明度),SRCCOLORKEY
(使用表面的透明颜色键来计算透明度)。
get_flags()
- 返回值
get_flags
方法的返回值是表示表面特性的整数。
from pygame import Surface, SRCALPHA
# 创建支持透明度的表面
s = Surface([400, 300], SRCALPHA)
# 通过 & 判断表面是否支持透明度
f = s.get_flags()
print(f'表面支持颜色透明度?{f & SRCALPHA == SRCALPHA}')
表面支持颜色透明度?True
获取和设置表面的透明度
如果Surface
表面对象被设置了透明度,那么表面中绘制的内容将被透明度影响,当然这还要取决于绘制时所采用的参数。
Surface
对象的get_alpha
方法,可用于获取表面的透明度。
get_alpha()
- 返回值
get_alpha
方法返回表示表面透明度的整数,范围从0
到255
。
Surface
对象的set_alpha
方法,可用于设置表面的透明度。
set_alpha(value)
set_alpha(value, flags=0)
- value 参数
value
参数是表示表面透明度的整数,范围从0
到255
,其中0
为完全透明,255
为不透明。这里需要特别说明,如果将
value
参数设置为空值None
,那么不但表面不再拥有透明度,像素中的透明度也不再生效,即便你在创建表面对象时,将构造器的flags
参数设置为了SRCALPHA
。- flags 参数
flags
参数可以被设置为pygame
模块的RLEACCEL
变量,以在未启用硬件加速的情况下提供更好的性能,这会导致表面在其内容被修改时的效率下降,但被其他表面渲染时的效率上升。
from pygame import Surface, SRCALPHA
# 创建支持透明度的表面,并设置表面的透明度
s = Surface((800, 600), SRCALPHA)
s.set_alpha(50)
print(f'表面的透明度:{s.get_alpha()}')
# 在表面绘制两个透明度不同的矩形
from pygame import draw
draw.rect(s, '#ff0000', [0, 0, 50, 50])
draw.rect(s, '#ff000099', [50, 0, 50, 50])
# 创建游戏窗口并绘制 s
from pygame import display
w = display.set_mode((800, 600))
w.blit(s, (0, 0))
display.flip()
import time
time.sleep(3)
表面的透明度:50
获取和设置表面的透明颜色键
如何在 Pygame 中使图像背景透明视频演示 YouTube如何在 Pygame 中使图像背景透明视频演示 Bilibili
如果Surface
表面对象拥有透明颜色键,并且所绘制内容的颜色与透明颜色键相同(颜色中的红色,绿色,蓝色相同),那么该内容将不会被绘制,无论表面对象自身是否支持颜色的透明度。
Surface
对象的get_colorkey
方法,可用于获取表面所使用的透明颜色键。
get_colorkey()
- 返回值
get_colorkey
方法的返回值是表示透明颜色键的形式类似于(r,g,b,a)
的 Python 元组,其中r
表示红色,g
表示绿色,b
表示蓝色,a
表示透明度(始终为255
)。如果表面对象没有设置透明颜色键,那么get_colorkey
方法会返回空值None
。
Surface
对象的set_colorkey
方法,可用于设置表面所使用的透明颜色键。
set_colorkey(color)
set_colorkey(color, flags=0)
- color 参数
color
参数是一个包含透明颜色键信息的对象,该对象的书写格式与Color
对象的构造器的rgbvalue
参数类似,颜色中的透明度始终被视为255
(不透明)。如果将color
参数设置为None
,则表示取消表面的透明颜色键。- flags 参数
flags
参数可以被设置为pygame
模块的RLEACCEL
变量,以在未启用硬件加速的情况下提供更好的性能,这会导致表面在其内容被修改时的效率下降,但被其他表面渲染时的效率上升。
在下面的示例中,我们将透明颜色键设置为红色,之后被绘制的第一个红色矩形将是完全透明的,无论其自身是否拥有透明度,第二个黄色矩形将是半透明的,因为其自身拥有并且表面支持透明度。
from pygame import Surface, SRCALPHA
# 创建支持透明度的表面,并将其透明颜色键设置为红色
s = Surface((800, 600), SRCALPHA)
s.set_colorkey(0xff0000)
print(f'透明颜色键:{s.get_colorkey()}')
# 在表面分别绘制红色和黄色的矩形
from pygame import draw
draw.rect(s, '#ff000088', [0, 0, 50, 50])
draw.rect(s, '#ffff0099', [50, 0, 50, 50])
# 创建游戏窗口并绘制 s
from pygame import display
w = display.set_mode((800, 600))
w.blit(s, (0, 0))
display.flip()
import time
time.sleep(3)
透明颜色键:(255, 0, 0, 17)
预先计算表面中的颜色的透明度
Surface
对象的premul_alpha
方法,可预先将表面中像素的颜色的透明度与红色,绿色,蓝色进行计算,并将计算结果作为新的表面对象返回。预先计算透明度对于混合模式BLEND_PREMULTIPLIED
非常有效,因为该模式认为颜色中的红色,绿色,蓝色已经与透明度进行了计算,这可以在一定程度上改进渲染的效率。
premul_alpha()
- 返回值
premul_alpha
方法的返回值是预先计算了透明度的新的Surface
表面对象。
premul_alpha 方法不会计算表面自身的透明度
Surface
对象的premul_alpha
方法的返回值不会受表面自身透明度的影响,即使通过set_alpha
方法为表面设置了有效的透明度,因为premul_alpha
只考虑表面中像素的透明度。关于set_alpha
方法,请参考获取和设置表面的透明度一段。
在下面的示例中,我们预先计算表面对象s
的透明度,得到了新的表面对象pa
,将两个表面对象同时绘制在游戏窗口中,他们展现了不同的透明度,因为premul_alpha
方法不考虑表面自身的透明度。
from pygame import Surface, SRCALPHA, BLEND_PREMULTIPLIED
# 创建一个表面并填充具有透明度的红色
s = Surface([100, 100], SRCALPHA)
s.fill([255, 0, 0, 100])
# 将表面自身的透明度设置为 50
s.set_alpha(50)
# 预先计算透明度
pa = s.premul_alpha()
# 创建游戏窗口并绘制 s 和 pa(pa 使用 BLEND_PREMULTIPLIED)
from pygame import display
w = display.set_mode((800, 600))
w.blit(s, (0, 0))
w.blit(pa, (100, 100), special_flags=BLEND_PREMULTIPLIED)
display.flip()
import time
time.sleep(3)
获取和设置表面的调色板
Surface
对象的get_palette
方法,可用于获取颜色深度为 8 的表面所使用的索引颜色(调色板)。
get_palette()
- 返回值
get_palette
方法的返回值是一个包含Color
对象的 Python 元组,每一个Color
对象对应一个索引颜色。
Surface
对象的get_palette_at
方法,可根据给出的索引获取调色板中的某个颜色,仅适用于颜色深度为 8 的表面。
get_palette_at(index)
- index 参数
index
参数是调色板中某个颜色的索引,取值范围是0
到255
。- 返回值
get_palette_at
方法的返回值是一个表示索引颜色的Color
对象。
Surface
对象的set_palette
方法,可用于设置颜色深度为 8 的表面所使用的索引颜色(调色板)。
set_palette(palette)
- palette 参数
palette
参数是一个包含索引颜色信息的 Python 序列对象,颜色信息的书写格式与Color
对象的构造器的rgbvalue
参数类似,但其不支持字符串和整数(0xRRGGBBAA
),并且颜色的透明度必须为255
(不透明)。这里需要说明,
palette
参数不必给出调色板中的全部 256 个索引颜色,比如,当只给出一个索引颜色时,则只有调色板的第一个索引颜色被修改,其余索引颜色不变。
Surface
对象的set_palette_at
方法,可根据给出的索引设置调色板中的某个颜色,仅适用于颜色深度为 8 的表面。
set_palette_at(index, color)
- index 参数
index
参数是调色板中某个颜色的索引,取值范围是0
到255
。- color 参数
color
参数是一个包含索引颜色信息的对象,该对象的书写格式与Color
对象的构造器的rgbvalue
参数类似,但其不支持字符串和整数(0xRRGGBBAA
),并且颜色中的透明度始终被视为255
(不透明)。
无法通过 get_palette,get_palette_at 方法所返回的 Color 对象修改调色板中的颜色
Surface
对象的get_palette
,get_palette_at
方法所返回的Color
对象是调色板颜色的副本,因此修改他们不会影响调色板中的原有颜色。
颜色
关于Color
构造器的rgbvalue
参数,你可以查看Pygame 中的 Color 对象一段。
在下面的示例中,我们将调色板的第一个颜色(黑色)修改为了红色,这将导致黑色背景的表面对象s
被显示为红色。
from pygame import Surface, Color
# 创建颜色深度为 8 的表面
s = Surface((800, 600), depth=8)
# 获取调色板
print(s.get_palette())
# 设置调色板中的第一个颜色(黑色)
s.set_palette([(255, 0, 0)])
# 获取调色板中的第一个颜色
c = s.get_palette_at(0)
print(c)
# 设置调色板中的第二个颜色
s.set_palette_at(1, Color('#00FF00'))
# 创建游戏窗口并绘制 s
from pygame import display
w = display.set_mode((800, 600))
w.blit(s, (0, 0))
display.flip()
import time
time.sleep(3)
((0, 0, 0, 255), (0, 0, 85, 255),…)
(255, 0, 0, 255)
获取和设置表面中的某个像素的颜色
Surface
对象的get_at
和get_at_mapped
方法,可根据给出的坐标,获取表面中某个像素的颜色。
get_at(x_y)
get_at_mapped(x_y)
- x_y 参数
x_y
参数是依次包含 X 坐标和 Y 坐标的 Python 整数序列对象。如果坐标所指示的位置超出了表面的范围,那么将引发异常IndexError
。- 返回值
get_at
方法的返回值是表示像素颜色的Color
对象,如果表面对象不支持透明度,那么所返回的Color
对象的透明度始终为255
。get_at_mapped
方法的返回值是表示像素颜色的映射整数。
Surface
对象的set_at
方法,可用于设置表面中某个像素的颜色。
set_at(x_y, color)
- x_y 参数
x_y
参数是依次包含 X 坐标和 Y 坐标的 Python 整数序列对象。如果坐标所指示的位置超出了表面的有效范围,那么set_at
方法不会产生任何影响。- color 参数
color
参数是一个包含颜色信息的对象,该对象的书写格式与Color
对象的构造器的rgbvalue
参数类似。如果表面对象使用了调色板,那么将在调色板中选择最接近于color
参数的颜色。
get_at,get_at_mapped,set_at 方法的性能问题
在循环中使用Surface
对象的get_at
,get_at_mapped
,set_at
方法,可能导致游戏出现性能问题,以上方法可能会临时锁定表面对象。
无法通过 get_at 方法所返回的 Color 对象修改像素的颜色
Surface
对象的get_at
方法所返回的Color
对象是像素的颜色的副本,因此对其进行修改不会影响原有像素的颜色。
在下面的示例中,我们通过set_at
方法在游戏窗口中绘制了一条白色的线段。
from pygame import Surface
# 创建表面,并获取坐标为 0 0 的像素的颜色
s = Surface((800, 600))
print(f'坐标 0 0 处的颜色:{s.get_at((0, 0))}')
# 绘制一条白色的线段
for i in range(100, 700):
s.set_at([i, 300], 'white')
print(f'坐标 100 300 处的颜色:{hex(s.get_at_mapped([100, 300]))}')
# 创建游戏窗口并绘制 s
from pygame import display
w = display.set_mode((800, 600))
w.blit(s, (0, 0))
display.flip()
import time
time.sleep(3)
坐标 0 0 处的颜色:(0, 0, 0, 255)
坐标 100 300 处的颜色:0xffffff
转换颜色与映射整数
颜色的映射整数,即颜色在表面中对应的整数,同一个颜色在不同的表面中,其映射整数可能不同,不同颜色在同一个表面的映射整数可能相同(比如,使用了调色板的表面中的两个相近的颜色)。要获取表面中某个坐标的颜色的映射整数,请参考获取和设置表面中的某个像素的颜色一段。
Surface
对象的map_rgb
方法,可获取指定颜色在表面中的映射整数。
map_rgb(color)
- color 参数
color
参数是一个包含颜色信息的对象,该对象的书写格式与Color
对象的构造器的rgbvalue
参数类似,但其不支持字符串和整数(0xRRGGBBAA
)。由于映射整数并不考虑透明度,因此颜色信息中的透明度将被忽略。- 返回值
map_rgb
方法的返回值是颜色的映射整数。
Surface
对象的unmap_rgb
方法,可获取指定映射整数在表面中对应的颜色。
unmap_rgb(mapped_int)
- mapped_int 参数
mapped_int
参数是颜色的映射整数。- 返回值
unmap_rgb
方法的返回值是一个Color
对象,表示映射整数对应的颜色。
此外,Surface
对象的get_masks
,get_shifts
,get_losses
方法,可用于获取颜色与映射整数转换所需的掩码,移位,有效位的信息。在正常的游戏开发中,这三个函数很少被使用。
get_masks()
get_shifts()
get_losses()
- 返回值
get_masks
,get_shifts
,get_losses
方法的返回值是一个形式类似于(r,g,b,a)
的 Python 整数元组,其中r
,g
,b
,r
分别表示红色,绿色,蓝色,透明度所对应的掩码,移位,有效位信息。
from pygame import Surface
# 创建颜色深度为 8 的表面
s1 = Surface([400, 300], depth=8)
# 两个不同颜色的映射整数相同
m1 = s1.map_rgb([255, 0, 0])
print(m1, s1.unmap_rgb(m1))
m2 = s1.map_rgb([250, 5, 5])
print(m2, s1.unmap_rgb(m2))
# 映射整数不考虑透明度
m3 = s1.map_rgb([255, 0, 0, 100])
print(m3, s1.unmap_rgb(m3))
# 创建颜色深度为 32 的表面
s2 = Surface([400, 300], depth=32)
# 相同的颜色在 s1 和 s2 中的映射整数不同
m3 = s2.map_rgb([255, 0, 0])
print(m3, s2.unmap_rgb(m3))
96 (255, 0, 0, 255)
96 (255, 0, 0, 255)
96 (255, 0, 0, 255)
16711680 (255, 0, 0, 255)
获取表面的边界
Surface
对象的get_bounding_rect
方法,可用于获取表面的边界,该边界是包含所有符合要求的像素的最小矩形,这些像素的颜色透明度大于或等于给出的最小透明度。
get_bounding_rect(min_alpha=1)
- min_alpha 参数
min_alpha
参数是表示最小透明度的整数,其取值范围不限于0
到255
。- 返回值
get_bounding_rect
方法的返回值是表示表面边界的Rect
对象。
get_bounding_rect 方法可能不会返回预期的结果
如果使用set_colorkey
方法为表面设置了透明颜色键,那么Surface
对象的get_bounding_rect
方法所返回的结果可能不符合你的预期,游戏边界可能等同于整个表面。关于方法set_colorkey
,请参考获取和设置表面的透明颜色键一段。
至于表面自身的透明度,并不会影响get_bounding_rect
方法的计算结果,因为表面自身的透明度并非像素颜色的透明度。关于表面透明度,请参考获取和设置表面的透明度一段。
get_bounding_rect 方法的性能问题
在循环中使用Surface
对象的get_bounding_rect
方法,可能导致游戏出现性能问题,该方法可能会临时锁定表面对象。
在下面的示例中,虽然我们将表面自身的透明度设置为了0
,但之后的表面边界依然被认定为不透明的矩形。
from pygame import Surface, SRCALPHA
# 创建支持透明度的表面
s = Surface((800, 600), SRCALPHA)
# 在表面绘制不透明与透明度为 1 的矩形
from pygame import draw
draw.rect(s, '#ff0000', [0, 0, 50, 50])
draw.rect(s, '#ff000001', [50, 0, 50, 50])
print(f'表面边界:{s.get_bounding_rect()}')
# 将表面自身的透明度设置为 0
s.set_alpha(0)
print(f'表面边界(透明度大于等于 2):{s.get_bounding_rect(2)}')
表面边界:<rect(0, 0, 100, 50)>
表面边界(透明度大于等于 2):<rect(0, 0, 50, 50)>
获取 Surface 表面对象所需的存储空间
Surface
对象的get_bitsize
方法,可用于获取表面的颜色深度,即需要多少个 bit 来存储一个像素(对应的颜色),该个数可能小于所需字节对应的 bit 个数。
get_bitsize()
- 返回值
get_bitsize
方法的返回值是一个整数,表示需要的 bit 个数。
Surface
对象的get_bytesize
方法,可用于获取存储一个像素(中的颜色)所需的字节数。
get_bytesize()
- 返回值
get_bytesize
方法的返回值是一个整数,表示需要的字节数。
Surface
对象的get_pitch
方法,可用于获取存储一行像素(中的颜色)所需的字节数。
get_pitch()
- 返回值
get_pitch
方法的返回值是一个整数,表示需要的字节数。
在下面的示例中,表面s
存储一个像素需要15
个 bit(2
个字节,其中一个 bit 没有被使用)。
from pygame import Surface
# 创建颜色深度为 15 的表面
s = Surface((100, 100), depth=15)
print(f'一个像素需要 {s.get_bitsize()} 个 bit,{s.get_bytesize()} 个字节')
print(f'一行像素需要 {s.get_pitch()} 个字节')
一个像素需要 15 个 bit,2 个字节
获取表面的缓冲区
Surface
对象的get_buffer
方法返回一个表示表面缓冲区的BufferProxy
对象,通过该对象可以直接访问或操作表面中的数据。get_buffer
方法将隐式的锁定表面对象,直至BufferProxy
对象被垃圾回收。
get_buffer()
- 返回值
get_buffer
方法返回表示缓冲区的BufferProxy
对象。
Surface
对象的_pixels_address
变量是一个整数,表示表面缓冲区的地址。
surface._pixels_address
from pygame import Surface
s = Surface((100, 100))
# 获取表面的缓冲区对象
b = s.get_buffer()
# 获取表面缓冲区的地址
print(f'缓冲区地址:{s._pixels_address}')
缓冲区地址:…
获取表面的缓冲区视图
Surface
对象的get_view
方法与get_buffer
方法类似,返回一个表示表面缓冲区的BufferProxy
对象,只不过该对象中的数据经过了筛选。
get_view(kind='2')
- kind 参数
kind
参数是一个字符串,表示如何对缓冲区中的数据进行筛选,可以是以下有效取值,'0'
,'1'
,'2'
,'3'
,'r'
,'g'
,'b'
,'a'
。- 返回值
get_view
方法返回一个BufferProxy
对象,该对象包含经过筛选的缓冲区数据。
复制表面
Surface
对象的copy
方法返回一个新的表面对象,该对象对应的表面的大小,特性,像素格式,调色板,自身透明度(并非透明颜色键)等,与原有表面相同。
copy()
- 返回值
copy
方法返回复制的Surface
表面对象。
在下面的示例中,虽然表面对象s1
设置了透明颜色键,但s2
的颜色透明键依然为空值None
。
from pygame import Surface, SRCALPHA
# 创建一个表面,然后复制他
s1 = Surface([400, 300], SRCALPHA, 16)
s1.set_colorkey('#ff0000')
s1.set_alpha(100)
s2 = s1.copy()
# s2 的透明颜色键为 None,与 s1 不同
print(f'复制的表面:{s2.get_size()} {s2.get_flags()} {s2.get_bitsize()} {s2.get_colorkey()} {s2.get_alpha()}')
复制的表面:(400, 300) 65536 16 None 100
内容分类
源码
讲解视频
如何在 Pygame 中获取表面大小及其矩形对象·YouTube如何在 Pygame 中获取表面大小及其矩形对象·Bilibili
如何在 Pygame 中获取和设置表面的透明度·YouTube如何在 Pygame 中获取和设置表面的透明度·Bilibili
如何在 Pygame 中使图像背景透明·YouTube如何在 Pygame 中使图像背景透明·Bilibili