pygame绘图功能留下像素宽的间隙。为什么?

时间:2014-02-25 00:05:05

标签: python pygame draw

将一段代码(动画一种矩形图案)从Java转换为Python之后,我注意到代码生成的动画看起来很奇怪。我设法用一个最小的例子重现问题如下:

import pygame
SIZE = 200
pygame.init()
DISPLAYSURF = pygame.display.set_mode((SIZE, SIZE))
D = 70.9
xT = 0.3
yT = 0
#pygame.draw.rect(DISPLAYSURF, (255,0,0),     (0, 0, SIZE, SIZE))
pygame.draw.rect(DISPLAYSURF, (255,255,255), (xT,   yT,   D, D))
pygame.draw.rect(DISPLAYSURF, (255,255,255), (xT+D, yT+D, D, D))
pygame.draw.rect(DISPLAYSURF, (0,0,0),       (xT,   yT+D, D, D))
pygame.draw.rect(DISPLAYSURF, (0,0,0),       (xT+D, yT,   D, D))
pygame.display.update()

此代码生成以下图像:

请注意,正方形在中间没有完美排列。取消注释上面代码中的注释行会产生以下图像,这可以进一步说明问题:

似乎在黑白图案中存在像素宽的间隙,即使在代码中可以看到(通过调用pygame.draw.rect()中传递的数据),这不应该不是这样的。这种行为的原因是什么,我该如何解决?

(这不是在Java中发生的,here是与上面的Python代码相对应的一段Java代码。)

1 个答案:

答案 0 :(得分:5)

在图像编辑器中查看渲染图片,可以确认像素距离:

Picture with measurements

扩展函数调用(即手动执行添加),可以看到绘制白色矩形的输入参数的格式为

pygame.draw.rect(DISPLAYSURF, (255,255,255), ( 0.3,    0, 70.9, 70.9))
pygame.draw.rect(DISPLAYSURF, (255,255,255), (71.2, 70.9, 70.9, 70.9))

由于像素的分数在屏幕上没有意义,因此输入必须以某种方式离散化。 Pygame(或SDL,如问题评论中所述)似乎选择截断,这实际上将绘图命令转换为:

pygame.draw.rect(DISPLAYSURF, (255,255,255), ( 0,  0, 70, 70))
pygame.draw.rect(DISPLAYSURF, (255,255,255), (71, 70, 70, 70))

,它对应于渲染图像中的尺寸。如果AWT以不同的方式绘制它,我的猜测是它使用舍入(某种)而不是截断。这可以通过尝试不同的渲染输入或通过挖掘文档进行调查。

如果想要像素完美渲染,则使用浮点作为输入的定义不明确。如果保持整数,结果应该独立于渲染器。


编辑:如果其他人发现这一点,我会扩展一下,因为除了源代码之外,我找不到有关此行为的更多信息。

有问题的函数调用采用以下输入参数(documentation):

pygame.draw.rect(Surface, color, Rect, width=0)

其中Rect是由左上角坐标,宽度和高度定义的特定对象。根据设计,它只处理整数属性,因为它意味着低级“这是你在屏幕上看到的”数据类型。数据类型通过截断来处理浮点数:

>>> import pygame
>>> r = pygame.Rect((1, 1, 8, 12))
>>> r.bottomright
(9, 13)
>>> r.bottomright = (9.9, 13.5)
>>> r.bottomright
(9, 13)
>>> r.bottomright = (11.9, 13.5)
>>> r.bottomright
(11, 13)

即,已完成常规(int)演员表。

Rect对象不是指“存储我的精灵的坐标”对象,而是“这是屏幕将代表的对象”对象。浮点对于前一个目的肯定是有用的,设计者可能希望保留一个内部浮动列表来存储这些信息。否则,通过例如增加屏幕位置。 r.left += 0.8(其中rRect个对象)永远不会移动r

问题中的问题来自(非常合理地)假设矩形的右x坐标至少会被计算为x₂ = int(x₁ + width),但由于函数调用隐式转换输入在继续之前将元组转换为Rect对象,并且由于Rect将截断其输入参数,因此它将计算为x₂ = int(x₁) + int(width),这对于float输入并不总是相同。

要使用舍入规则创建Rect,可以使用例如定义一个包装器,如:

def rect_round(x1, y1, w, h):
    """Returns pygame.Rect object after applying sane rounding rules.

    Args:
        x1, y1, w, h:
            (x1, y1) is the top-left coordinate of the rectangle,
            w is width,
            h is height.

    Returns:
        pygame.Rect object.
    """
    r_x1 = round(x1)
    r_y1 = round(y1)
    r_w = round(x1 - r_x1 + w)
    r_h = round(y1 - r_y1 + h)
    return pygame.Rect(map(int, (r_x1, r_y1, r_w, r_h)))

(或针对其他舍入规则进行修改)然后调用draw函数,例如。

pygame.draw.rect(DISPLAYSURF, (255,255,255), rect_round(71.2, 70.9, 70.9, 70.9))

但是,永远不会绕过这样一个事实:按照定义,像素是屏幕上最小的可寻址单元,所以这个解决方案也可能有它的怪癖。


2005年Pygame邮件列表上的相关主题:Suggestion: make Rect use float coordinates