Problem in understanding operation on color python

Viewed 46

I'm start learning pygame i buy a book, in this book i have this script:

import pygame
pygame.init()


screen = pygame.display.set_mode((640,480))

all_colors = pygame.Surface((4096,4096),depth=24)

for r in xrange(256):
    x = (r & 15) * 256
    y = (r >> 4) * 256
    print x, "      " , y
    for g in xrange(256):
        for b in xrange(256):
            all_colors.set_at((x+g,y+b),(r,g,b))
    screen.blit(all_colors,(0,0))

pygame.image.save(all_colors,"allcolors.bmp")

In this script we produce an image that contains all colors that our computer can produce,the only instructions i don't understand are:

  1. x = (r & 15) * 256

  2. y = (r >> 4) * 256

Someone coud explane me why he make this 2 operation. I know that are bitwise operator but i don't understand why he do that thanks :D .

2 Answers

& is the bitwise And operator. 15 is 00001111 binary. Hence (r & 15) masks the lower 4 bits of r. The result is in range [0, 15].
>> is the right shift operator. (r >> 4) shift r by 4 bits to the right. If r is a byte (8 bit) value, the result is again in range [0, 15].
(r & 15) are the lower 4 bits of r and (r >> 4) are the higher 4 bits of r (if r is in range [0, 255]).

The for loop

for r in xrange(256):
    x = (r & 15) * 256
    y = (r >> 4) * 256

can be substituted by the following nested for loops:

for i in xrange(16):
    for j in xrange(16):
        r = i * 16 + j
        x = j * 256
        y = i * 256  

The surface has a size of 4096x4096. 4096 = 16*256. x and y are both in range [0, 4096).

This is really confusingly written IMO. What's happening is that x and y (which define the location in a grid on the screen) are derived from r by taking the upper and lower 4 bits of each possible r value, but this isn't really obvious and you shouldn't feel bad for not getting it as a beginner; the author should feel bad for writing tricky code and not commenting it. :)

IMO if you're doing bitwise stuff it's better to represent the numbers as hex, since that makes it easier to visualize the shifts (e.g. if you multiply something by 0x100 in hex it's more obvious that the goal is to shift two hex digits to the left vs if you multiply by decimal 256). I also think it'd be better to have the outer loop go through x and y and generate r from those values, rather than doing it the other way around, since the math is simpler that way.

Here's how I might write that loop (using hexadecimal numbers, with comments). I've also written it to be Python 3 compatible, but I think it ought to be backward compatible with Python 2 as well:

for x in range(0x10):
    for y in range(0x10):
        # x and y define a 0x10 by 0x10 grid on the screen.
        print(x, "      " , y)
        # Each cell in the grid has a unique 8-bit red value (r) based on its x, y.
        r = x * 0x10 + y  
        # Each cell is itself 0x100 pixels square with each pixel having a unique g, b.
        for g in range(0x100):
            for b in range(0x100):
                # g and b serve as both color values and cell-relative coordinates.
                # To get the screen-relative coords we add the x/y values for the cell,
                # multiplied by the dimensions of the cell in pixels (0x100).
                all_colors.set_at((x * 0x100 + g, y * 0x100 + b), (r, g, b))
        screen.blit(all_colors, (0, 0))
Related