TL; DR:使用 (py)sdl2,我正在尝试绘制一个圆形轮廓,其内部是透明的,因此显示了在其后面绘制的对象。基本上我需要找到一种方法来擦除填充圆圈内部的像素(或将它们绘制成透明像素/将它们设置回透明像素)。我想尽可能使用纹理而不是表面。
我正在尝试实现一些在概念上看起来非常简单的东西,但我就是无法实现。
我想在 python 中用 sdl2 绘制一个圆形轮廓。当然,这用 sdl2gfx 函数实现起来很简单circleRGBA()
,但它只允许你画出线宽为 1px 的轮廓。用较粗的轮廓画圆似乎是完全不可能的。
我之前使用透明颜色键(受此方法启发)使用 pygame 表面做过类似的事情,我知道表面也可用于 SDL2,但需要注意的是我试图坚持使用更快的纹理,这确实不提供色键机制。
为了在视觉上更好地解释事情:我想要实现的是:
一个大于 1px 宽的圆(环),其内部是透明的,这样在圆后面绘制的项目将在内部可见。
我过去使用的一个技巧是只绘制 2 个实心圆圈,其中第二个较小的圆圈与背景颜色相同。但是,这会掩盖圆圈后面的所有内容(在本例中为白线)。下面是一个使用这种方法的函数示例:
@to_texture
def draw_circle(self, x, y, r, color, opacity=1.0, fill=True, aa=False, penwidth=1):
# Make sure all spatial parameters are ints
x, y, r = int(x), int(y), int(r)
# convert color parameter to sdl2 understandable values
color = sdl2.ext.convert_to_color(color)
# convert possible opacity values to the right scale
opacity = self.opacity(opacity)
# Get background color set for this texture
bgcolor = self.__bgcolor
# Check for invalid penwidth values, and make sure the value is an int
if penwidth != 1:
if penwidth < 1:
raise ValueError("Penwidth cannot be smaller than 1")
if penwidth > 1:
penwidth = int(penwidth)
# if the circle needs to be filled, it's easy
if fill:
sdlgfx.filledCircleRGBA(self.sdl_renderer, x, y, r, color.r, color.g, color.b, opacity)
else:
# If penwidth is 1, simple use sdl2gfx own functions
if penwidth == 1:
if aa:
sdlgfx.aacircleRGBA(self.sdl_renderer, x, y, r, color.r, color.g, color.b, opacity)
else:
sdlgfx.circleRGBA(self.sdl_renderer, x, y, r, color.r, color.g, color.b, opacity)
else:
# If the penwidth is larger than 1, things become a bit more complex.
outer_r = int(r+penwidth*.5)
inner_r = int(r-penwidth*.5)
# Draw outer circle
sdlgfx.filledCircleRGBA(self.sdl_renderer, x, y,
outer_r, color.r, color.g, color.b, opacity)
# Draw inner circle
sdlgfx.filledCircleRGBA(self.sdl_renderer, x, y,
inner_r, bgcolor.r, bgcolor.g, bgcolor.b, 255)
return self
导致:
我还尝试了各种混合模式,这是我能得到的最好结果:
@to_texture
def draw_circle(self, x, y, r, color, opacity=1.0, fill=True, aa=False, penwidth=1):
# ... omitted for brevity
else:
# If the penwidth is larger than 1, things become a bit more complex.
# To ensure that the interior of the circle is transparent, we will
# have to work with multiple textures and blending.
outer_r = int(r+penwidth*.5)
inner_r = int(r-penwidth*.5)
# Calculate the required dimensions of the separate texture we are
# going to draw the circle on. Add 1 pixel to account for division
# inaccuracies (i.e. dividing an odd number of pixels)
(c_width, c_height) = (outer_r*2+1, outer_r*2+1)
# Create the circle texture, and make sure it can be a rendering
# target by setting the correct access flag.
circle_texture = self.environment.texture_factory.create_sprite(
size=(c_width, c_height),
access=sdl2.SDL_TEXTUREACCESS_TARGET
)
# Set renderer target to the circle texture
if sdl2.SDL_SetRenderTarget(self.sdl_renderer, circle_texture.texture) != 0:
raise Exception("Could not set circle texture as rendering"
" target: {}".format(sdl2.SDL_GetError()))
# Draw the annulus:
# as the reference point is the circle center, outer_r
# can be used for the circles x and y coordinates.
sdlgfx.filledCircleRGBA(self.sdl_renderer, outer_r, outer_r,
outer_r, color.r, color.g, color.b, opacity)
# Draw the hole
sdlgfx.filledCircleRGBA(self.sdl_renderer, outer_r, outer_r,
inner_r, 0, 0, 0, 255)
# Set renderer target back to the FrameBuffer texture
if sdl2.SDL_SetRenderTarget(self.sdl_renderer, self.surface.texture) != 0:
raise Exception("Could not unset circle texture as rendering"
" target: {}".format(sdl2.SDL_GetError()))
# Use additive blending when blitting the circle texture on the main texture
sdl2.SDL_SetTextureBlendMode(circle_texture.texture, sdl2.SDL_BLENDMODE_ADD)
# Perform the blitting operation
self.renderer.copy( circle_texture, dstrect=(x-int(c_width/2),
y-int(c_height/2), c_width, c_height) )
return self
这导致:
关闭,但没有雪茄,因为这条线现在似乎在圆圈的前面而不是在它的后面,据我所知,添加剂/屏幕混合,这是预期的行为。
我知道还有函数SDL_SetRenderDrawBlendMode,但是 sdl2gfx 绘图函数似乎忽略了您使用此函数设置的任何内容。
有没有比我更有经验的人以前做过类似的事情,谁能为我指明如何应对这一挑战的正确方向?