Pygame 和绘图:白色在白色上 = 灰色?
我正在使用pygame(版本1.9.0rc3,1.8.1也会出现同样的问题)来制作热图。为了制作热图,我使用了一张小的24位11x11像素的PNG图片,这张图片有一个白色的背景和一个低透明度的灰色点,点的边缘刚好停在边界上:
点的图片 http://img442.imageshack.us/img442/465/dot.png
点周围的区域是完美的白色,颜色代码是#ffffff,这本来是对的。然而,当我用pygame多次将这张图片叠加到一个新的表面上,并使用BLEND_MULT模式时,出现了一个灰色的方块,看起来像是点的背景不是完美的白色,这就让人困惑了。
以下代码和附带的图片可以重现这个问题:
import os
import numpy
import pygame
os.environ['SDL_VIDEODRIVER'] = 'dummy'
pygame.display.init()
pygame.display.set_mode((1,1), 0, 32)
dot_image = pygame.image.load('dot.png').convert_alpha()
surf = pygame.Surface((100, 100), 0, 32)
surf.fill((255, 255, 255))
surf = surf.convert_alpha()
for i in range(50):
surf.blit(dot_image, (20, 40), None, pygame.BLEND_MULT)
for i in range(100):
surf.blit(dot_image, (60, 40), None, pygame.BLEND_MULT)
pygame.image.save(surf, 'result.png')
当你运行这段代码时,你会得到以下的图片:
叠加后的结果图片 http://img263.imageshack.us/img263/4568/result.png
为什么会发生这种情况呢?我该如何解决这个问题呢?
2 个回答
我在制作热图的时候也遇到了这个问题,读了balpha的回答后,决定用“正确”的方式来解决这个问题(虽然速度可能会慢一些)。我把各种
(s * d) >> 8
改成了
(s * d) / 255
这需要对alphablit.c
中的多个乘法函数进行修改(虽然我也修改了surface.h
)。我不太确定这对性能的影响有多大,但对于特定的热图应用来说,它生成的图像要漂亮得多。
经过一番尝试,我发现你说得完全正确。用255去乘总是会导致减去1——每次都是这样。最后,我下载了pygame的源代码,答案就在surface.h
里:
#define BLEND_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \
dR = (dR && sR) ? (dR * sR) >> 8 : 0; \
dG = (dG && sG) ? (dG * sG) >> 8 : 0; \
dB = (dB && sB) ? (dB * sB) >> 8 : 0;
Pygame实现的混合方式是
new_val = old_dest * old_source / 256
而不是,这才是正确的方式,应该是
new_val = old_dest * old_source / 255
这样做可能是为了优化,因为位移运算比除法要快得多。由于255 / 256
这个比例非常接近1,所以造成的唯一差别就是“少了1”:你得到的值是预期值减去1——当然,如果你预期的是0,那结果就是正确的。
所以,你有以下几种选择:
- 忽略它,因为对于大多数情况来说,少1并不重要。
- 在所有结果值上加
1
。这样最接近预期结果,但你会失去0。 - 如果整体正确性不重要,但你需要
255 * 255 == 255
(你懂我意思的),那么用1
进行或运算而不是加法就足够了,而且更快。
注意,如果你不选择第一种答案,出于性能考虑,你可能需要写一个C扩展,而不是直接使用Python。