有没有办法为索引的pygame.Surfaces合并调色板? [英] Is there a way to merge palettes for indexed pygame.Surfaces?

查看:230
本文介绍了有没有办法为索引的pygame.Surfaces合并调色板?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有索引调色板的Surface BaseSurf ,我想要 blit 另一个索引颜色Surface它(我们称之为 NewSurf )。但是,BaseSurf的调色板与NewSurf不同,而且pygame不会自动将缺少的颜色添加到BaseSurf的调色板中。
因此我需要一些方法将NewSurf的颜色附加到BaseSurf的调色板上而不会损坏(覆盖Surface的像素实际使用的调色板索引)。

I have a Surface BaseSurf with an indexed colour palette, and I want to blit another indexed-colour Surface to it (let's call it NewSurf). BaseSurf's palette is different from NewSurf's, though, and pygame does not automatically add the missing colours to BaseSurf's palette. I therefore need some way to append NewSurf's colours to BaseSurf's palette without mangling either (overwriting palette indices which are actually used by the Surface's pixels).

当我运行此代码:

screen = pygame.display.set_mode((222,222))
screen.fill((255,255,255))

BaseSurf = pygame.load.image("4samps.png").convert(8)  # indexed with four colours; larger dimensions
NewSurf  = pygame.load.image("another4samps.png").convert(8)  # indexed with four more colours

screen.blit(BaseSurf, (10,10))
screen.blit(NewSurf, (160,43))

BaseSurf.blit(NewSurf, (33,33))

screen.blit(BaseSurf, (50,122))

......这就是我所看到的......

...this is what I see...

- 编辑:请注意,即使序列关闭,组合图形也会将NewSurf中的颜色近似为BaseSurf正在使用的颜色。右下方的blob实际上是一个空值(0,0,0,255),是最接近的匹配颜色!

-- Notice that the combined graphic approximates the colours in NewSurf to whatever BaseSurf is using, even if the sequence is off. The bottom right blob actually takes a null value (0,0,0,255) to be the closest matching colour!

显然,NewSurf(右侧)有其索引转移到BaseSurf,它不是不正确的,但也不是我想要的。
如何将NewSurf blit到BaseSurf,在修改后的图像中保留其精确的颜色数据(但保留索引的彩色图像)?

Obviously, NewSurf (on the right) has had its indices transferred to BaseSurf, which is not incorrect exactly, but is also not what I want. How can I blit NewSurf to BaseSurf, preserving its exact colour data in the revised image (but retaining an indexed colour image)?

这是另一个(未答复的)问题。

推荐答案

我在询问过程中确实找到了答案。

I actually figured out an answer in the course of asking it.

使用 pygame.Surface.set_palette_at(),我能够从NewSurf中提取调色板数据(使用 NewSurf.get_palette(..)和迭代器清除调色板的空白)并使用 BaseSurf.set_palette_at(first_null_RGBA_index,将其粘贴到BaseSurf调色板的'end'(即最后一个非空白RGBA索引值之后) new_RGBA_value)

Using pygame.Surface.set_palette_at(), I was able to extract the palette data from NewSurf (using NewSurf.get_palette(..) and an iterator to clear out that palette's blanks) and paste it onto the 'end' (that is, after the last non-blank RGBA index value) of BaseSurf's palette using BaseSurf.set_palette_at(first_null_RGBA_index, new_RGBA_value).

您可以通过多种方式找到 first_null_RGBA_value ;我用 next()来查找第一个空值(由 blankRGBA 定义,通常是(0, 0,0,255)根据我的经验)在 c> get_palette()中的使用中调色板之后的未使用值的训练中。您还可以使用 get_palette()。index(blankRGBA)来获取第一个空白值的索引(如果你实际上是超级风险的话)在图形中有空值像素!)。我将在稍后描述方法及其问题(如我所见)。

You can find first_null_RGBA_value in a number of ways; I've used next() to find the first null value (defined by blankRGBA, which is usually (0,0,0,255) in my experience) in the 'train' of null (unused) values that follows the in-use palette in get_palette(). You could also use get_palette().index(blankRGBA) to get the index of the first blank value (which is super-risky if you actually have blank-value pixels in the graphic!). I'll describe the methods and their issues (as I see them) in a moment.

碰巧,我似乎似乎必须重新索引NewSurf它的索引与BaseSurf调色板中的新位置对齐。 我不知道如果重新映射重复的RGB值会产生什么后果!我想pygame会从blit'd图像中近似RGB值来匹配接收中的最接近的值 - 一个确切的匹配,如果你事先将NewSurf的整个调色板有目的地粘贴到BaseSurf中。

As it happens, I do not seem to have to reindex NewSurf that it's indices align to their new locations in BaseSurf's palette. I do not know what consequence this will have if you remap a duplicated RGB value! I imagine that pygame approximates RGB values from the blit'd image to match the closest ones in the receiving one-- an exact match, if you purposefully paste the whole palette from NewSurf into BaseSurf beforehand.

这就是我的表现。

   ... # initialized and blitted samples as before...
blankRGBA = (0,0,0,255)  # the RGBA value for null values in the index.

destpal   = list(BaseSurf.get_palette())
pallength = len(destpal) - 1 # This is probably always going to be 255, but just to be sure... (minus one 'cuz indices start at zero.)

nextblank = pallength - next((n for n, RGBA in enumerate(destpal[::-1]) if RGBA != blankRGBA), - 1)   
    #  ^ The palette will have a train of null values matching blankRGBA for every unusued index value. If the palette is complete full, it'll raise an error later.
    # This finds the index of the first such value by following the train until it 'starts', then subtracting that index number from the total length (probably 256). I'll explain why destpal.index(blankRGBA) is chancey later...

copypal = list(NewSurf.get_palette())
while True:
    if copypal[-1] == blankRGBA:  # Get rid of NewSurf's null index train, too.
        copypal.pop()
    else:
        print "Popped all trailing blank values from the incoming palette. %d entries remain." % len(copypal)
        break

    if not copypal:
        raise IndexError, "Depleted incoming palette. It was entirely blank entries?! What??"

    # Now that we have the useful section of NewSurf's palette (copypal) and the indices we can replace with it (nextblank), let's apply the new values and reindex NewSurf ahead of blitting it...

for n, newRGBA in enumerate(copypal):  
    if (nextblank + n) > 255: # It's possible the thing will fill up. For now, I'll have it throw an error.
        raise IndexError, "Ran out of palette space at %s! (colour number %d)" % (newRGBA, n)
    BaseSurf.set_palette_at((nextblank + n), newRGBA)  # Add the palette value to BaseSurf. As it happens, blit will reindex the colours on its own.

baseimage.blit(newimage, (33,33))
screen.blit(baseimage, (50, 122))

pygame.display.flip()

我可能还可以使用类似 copypal = list(RGBA)清除NewSurf调色板中的空白RGBA值对于RGBA in NewSurf.get_palette()if RGBA!= blankRGBA)而不是而True; ... copypal.pop()循环。我也可以使用 destpal.index(blankRGBA)找到BaseSurf调色板中的第一个空白,而不是更复杂的 next()指令。之所以我没有这样做,是因为调色板有可能在图像中使用至少一个像素 blankRGBA 值,并且这些像素是空白的 - 在图像中的某个地方使用(0,0,0,255)并非不太可能。

I could probably have also cleaned the blank RGBA values from NewSurf's palette with something like copypal = list(RGBA for RGBA in NewSurf.get_palette() if RGBA != blankRGBA) instead of the while True; ... copypal.pop() loop. I also could have found the first blank in BaseSurf's palette using destpal.index(blankRGBA) instead of the more complicated next() instruction. The reason why I did neither of these is because there's a chance that the palettes used the blankRGBA value for at least one pixel in the images, and that these pixels were intended to be blank-- it's not so unlikely that (0,0,0,255) would be used somewhere in the image.

据推测,如果是在这种情况下,RGBA索引将位于调色板的 start 而不是 end 。如果它们只是最后一个索引,那么它们就是安全的。否则,可能会出现问题。

Presumably, if that were the case, the RGBA indices would be at the start of the palette instead of at the end. If they were anywhere but the last index, they'll be safe. Otherwise, there could be a problem.

对于可以非常接近地控制图像数据的情况,这些缩小版本也可以工作。

但请注意,由于它们更难以阅读,它们很可能不那么Pythonic,并且它们更容易受到某些影响问题(意外地替换实际使用的看似空白的索引值)和未处理的异常(用完BaseSurf中的调色板空间)。

Note however that they are very probably less Pythonic as they're harder to read, and that they are more vulnerable to certain problems (accidentally replacing a null-looking index value that's actually in use) and unhandled exceptions (running out of palette space in BaseSurf).

使用风险由您自负! / p>

Use at your own risk!

  # init as before...
for newRGBA in tuple(RGBA for RGBA in NewSurf.get_palette() if RGBA != blankRGBA):
    try: swapidx = next(n for n, RGBA in enumerate(BaseSurf.get_palette()) if RGBA == blankRGBA)
    except StopIteration: raise IndexError, "Ran out of palette space!"
    BaseSurf.set_palette_at(swapidx, newRGBA)

BaseSurf.blit(NewSurf, (33,33))
screen.blit(BaseSurf, (50, 122))

这有点慢,因为 next()将遍历copypal中每个值的调色板,但是,它是更少的行,这是一个 good 的东西。

This is a little slower, since next() will iterate through the palette for each value in copypal, but hey, it's fewer lines, and that's a good thing.

你甚至可以使用这个可怕而且非常不顺便的单身人士:

You could even just use this horrible and extremely unPythonic singleton:

[BaseSurf.set_palette_at(swapidx, newRGBA) for swapidx, newRGBA in zip(tuple(n for n, RGBA in enumerate(BaseSurf.get_palette()) if RGBA == blankRGBA), tuple(RGBA for RGBA in NewSurf.get_palette() if RGBA != blankRGBA))]  # PLEASE GOD NO.

请注意,我推荐短版本。我只是将它们包括在内以供学术考虑。

Note that I do not recommend either short version. I've only included them for academic consideration.

这篇关于有没有办法为索引的pygame.Surfaces合并调色板?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆