Red channel 0

From Viki
Jump to navigation Jump to search

Red channel 0 is an oversight with the collision and the texture of a sprite that is 32 by 32 pixels or less in size.

VVVVVV uses the literal texture of a sprite for collision between two sprites that are both 32 by 32 pixels or less in size. Each pixel is checked, and if a pixel overlaps, the two entities are considered to be colliding.

However, the check that a pixel exists is flawed. It tries to check if the alpha channel of a pixel is greater than zero, but actually checks the red channel. Therefore, if the red channel of a pixel is zero, then it's considered non-existent.

Given that sprites are always recolored when drawn on screen, it is impossible to tell if a drawn pixel of a custom sprite is actual collision, without looking at the spritesheet in an image editor, due to this oversight.

Note that fully transparent pixels are still always non-existent, even if their red channel isn't 0. This is because somewhere during the sprite texture loading process, all fully transparent pixels have their RGB zeroed out (likely in LodePNG). However, a virtually-invisible pixel that still collides is still possible if the pixel is given a minimum of transparency along with a nonzero red channel; such a pixel is indistinguishable to the naked eye.

This oversight can be utilized in custom levels to make things such as harmless enemies.

Code analysis

From the initial commit of the VVVVVV repository, Graphics.cpp line 1236:

1236                 Uint32 pixel1 = ReadPixel(surface1 , x - p1.x, y - p1.y);
1237                 Uint32 pixel2 = ReadPixel(surface2 , x - p2.x, y - p2.y);
1238                 if ((pixel1 & 0x000000FF) && (pixel2 & 0x000000FF))
1239                 {
1240                     return true;
1241                 }

The pixels here are stored as a 4-byte number, with each byte being one channel, which is one of red, green, blue, or alpha.

Line 1238 is the flawed check. The code uses a mask and assumes the last byte is alpha, assuming that the bytes are in RGBA order. However, they are actually in ABGR order. So it actually ends up reading the red channel instead.

A better solution would have been to use the SDL-provided surface1->format->Amask and surface2->format->Amask instead of hardcoding 0x000000FF. Or, keep the pixel color masks consistent throughout the codebase. At this point in time, the codebase was a mess, interchanging ABGR and ARGB and such in various places.


This oversight only exists since 2.2 with the switch to using LodePNG for reading images instead of SDL_image. 2.1 and 2.0 have a different oversight with sprite texture data and collision.

In 2.1 and 2.0, a collision pixel exists if any RGB channel is greater than zero. This means that a pixel is only non-existent if all of its RGB channels are zero. Note that this does not include the alpha channel. Therefore, in 2.1 and 2.0 only, it's possible to have a fully transparent pixel that still collides.

This has implications for speedrunning. A strat in the room "Vertigo" in the Final Level, Lightning Vertigo, is impossible on 2.1 and 2.0, because the enemies have some pixels that have nonzero RGB value.