December 15th, 2011
If you’ve ever seen this kind of syntax and been bewildered:
@decorator
def function( x ):
return x
Then you are not alone. This is an example of using decorators in python. The @ indicates that the function will be “decorated” with “decorator.” What is decorator exactly?
It’s a function that takes a function as an argument and returns a function.
def decorator( your_function ):
return math.sin
@decorator
def function( x ):
return x
You have just made a decorator. The function “function” is now whatever “decorator” returns, which is in this case math.sin. Now when you run:
print function( 5 )
You will really be calling math.sin( 5 ), because that’s what your decorator returned. Of course, this is a silly example because it does not make use of your function. Let’s make one that does.
def decorator( your_function ):
def new_function( x ):
return 5 + your_function( x )
return new_function
@decorator
def function( x ):
return x
Don’t be thrown off by the fact that I defined a function within a function. This is just fine by the Python compiler.
What’s happening here is that I defined a function called new_function that takes a single argument. It calls your function, giving it the value x, and then adds 5 to it and returns it. At the end of the decorator function, new_function is returned. Now when you call
print function( 10 )
You will see 15. What you really called was the function new_function, with an argument 10. That’s because the function decorator returned new_function, which runs your old function and adds 5 to it. So the entire code would be this:
def decorator( your_function ):
def new_function( x ):
return 5 + your_function( x )
return new_function
@decorator
def function( x ):
return x
print function(5)
I hope that was a good explanation for some tricky Python stuff.
No Comments
December 15th, 2011
I am using LibPNG and Zlib in my game engine. After much frustration, I was finally able to get it to work. The following is my LoadImage function:
void Image::LoadImage( const char *filename ) {
// Open up the file as a binary
FILE *file_pointer = fopen( filename, "rb");
if( file_pointer == 0 ) {
Engine::console << "LoadImage failed: file '" << filename << "'does not exist." << std::endl;
// The file does not exist. Create a false image that is just red.
GenerateImage( 64, 64, 255, 128, 128, 255 );
return;
}
// First we check the header, stored in the first 8 bytes of the file
char header[8];
fread(header, 1, 8, file_pointer );
// png_sig_cmp will return 0 if the file read is not a PNG
if (png_sig_cmp( (png_const_bytep) header, 0,
!= 0 ) {
Engine::console << "LoadImage failed: file '" << filename << "' is not a PNG." << std::endl;
GenerateImage( 64, 64, 255, 128, 128, 255 );
fclose( file_pointer );
return;
}
// Initialize the two structures that will represent the PNG file.
png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
png_infop info_ptr = png_create_info_struct( png_ptr );
// This is an old C way to handle errors, and franky is not very good because it
// can jump over class destructors if you're not careful.
if ( setjmp( png_jmpbuf(png_ptr) ) ) {
Engine::console << "LoadImage failed: file '" << filename << "' caused LibPNG to trigger an error response." << std::endl;
GenerateImage( 64, 64, 255, 128, 128, 255 );
// This block is called if there was an error reading the file
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
if ( file_pointer ) { fclose( file_pointer); }
return;
}
// Initialize the reading of the file, skip the 8 signature bytes, and then read the file information
png_init_io( png_ptr, file_pointer );
png_set_sig_bytes( png_ptr, 8 );
png_read_info( png_ptr, info_ptr );
// Get the information, including image width, height, bit depth and color type.
// Bit depth is the number of bits per channel, a channel being red, green, blue, or alpha.
// Typically bit depth will be 8, meaning each channel gets 1 byte.
image_width = png_get_image_width ( png_ptr, info_ptr );
image_height = png_get_image_height( png_ptr, info_ptr );
int bit_depth = png_get_bit_depth( png_ptr, info_ptr );
int color_type = png_get_color_type( png_ptr, info_ptr );
int channels = png_get_channels(png_ptr, info_ptr);
// Allocate the data for the texture using its width and height. Reserve 4 bytes for RGBA pixels.
image_data = new unsigned char[ image_width * image_height * 4 ];
// Read the pixel data from the PNG into an array of rows.
// First allocate memory for row structure.
png_bytepp row_pointers = new png_bytep[image_height];
// Then go through each row, and allocate space for that row.
for ( int i = 0; i < image_height; i++) {
row_pointers[i] = new png_byte[ image_width * channels ];
}
// Finally, read the image into the row structure we just made.
png_read_image(png_ptr, row_pointers);
// Copy the image data into the image class's image_data, which
// must be an array of RGBA pixels.
for( int y = 0; y < image_height; y++ ) {
// Get one row of pixels for the row structure
png_bytep row = row_pointers[y];
for( int x = 0; x < image_width; x++ ) {
// Our data is stored in image_data, and the PNG pixels are stored in row
image_data[ y * image_width * 4 + x*4 + 0 ] = row[ x * channels + 0 ];
image_data[ y * image_width * 4 + x*4 + 1 ] = row[ x * channels + 1 ];
image_data[ y * image_width * 4 + x*4 + 2 ] = row[ x * channels + 2 ];
// If there is an alpha channel, be sure to copy it
if( channels == 4 ) {
image_data[ y * image_width + x*4 + 3 ] = row[ x * channels + 3 ];
} else {
image_data[ y * image_width + x*4 + 3 ] = 255;
}
}
}
// Free all memory that we used to avoid leaks, starting with each individual row
for ( int i = 0; i < image_height; i++) {
delete [] row_pointers[i];
}
// And finally, delete the row structure
delete [] row_pointers;
// Final PNG closing things
png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
// Close our file pointer
fclose( file_pointer );
}
There are some premade things in this snippet. For one, the GenerateImage function just creates a blank, solid color image instead of a loaded PNG, in the case of an error. Engine::console is just a singleton that outputs to some error stream, usually a file. Finally, image_data is an unsigned char array. I allocated 4 bytes per pixel so that the buffer is always RGB.
If you’re getting crashes with LibPNG, make sure you are linking the right library: compile LibPNG with the same compiler options as your project. This is due to the fact that LibPNG uses some internal functions to read from a file pointer.
Next time I might just use stb_image.
No Comments