# # Surface.pm # # A package for manipulating SDL_Surface * # # David J. Goehrig Copyright (C) 2000 package SDL::Surface; use strict; use SDL::sdlpl; require SDL::Rect; require SDL::Palette; require Exporter; # # Surface Constructor / Destructor # sub new { my $proto = shift; # Std constructor my $class = ref($proto) || $proto; my $self = {}; my %options = @_; # Usage: new Surface ( if ( exists($options{-name}) ) { # -name => "yomama.jpg" ); to load and image else # Loads, bmp, ppm, pcx, gif, jpeg, png images $self->{-surface} = SDL::sdlpl::sdl_new_surface($options{-name},0,0,0,0,0,0,0,0); } elsif (exists($options{-from})) # create from a memory block (typically another XS lib (i.e. PDL) with 'fixed' memory locations! { my $w = $options{-width} || 0; # -width => 800, to set the width my $h = $options{-height} || 0; # -height => 600, to set the height my $d = $options{-depth} || 0; # -depth => 8, to set the bitsperpixel my $p = $options{-pitch} || 0; my $r = $options{-Rmask} || 0x000000ff; # -Rmask => 0xff000000, for big endian machines my $g = $options{-Gmask} || 0x0000ff00; # -Gmask => 0x00ff0000, ditto my $b = $options{-Bmask} || 0x00ff0000; # -Bmask => 0x0000ff00, ditto my $a = $options{-Amask} || 0xff000000; # -Amask => 0x000000ff ); ditto $self->{-surface} = SDL::sdlpl::sdl_new_surface_from($options{-from},$w,$h,$d,$p,$r,$g,$b,$a); } else { my $f = $options{-flags} || SDL::sdlpl::sdl_anyformat(); # -flags => SDL_ANYFORMAT, to set the flags my $w = $options{-width} || 0; # -width => 800, to set the width my $h = $options{-height} || 0; # -height => 600, to set the height my $d = $options{-depth} || 0; # -depth => 8, to set the bitsperpixel my $r = $options{-Rmask} || 0x000000ff; # -Rmask => 0xff000000, for big endian machines my $g = $options{-Gmask} || 0x0000ff00; # -Gmask => 0x00ff0000, ditto my $b = $options{-Bmask} || 0x00ff0000; # -Bmask => 0x0000ff00, ditto my $a = $options{-Amask} || 0xff000000; # -Amask => 0x000000ff ); ditto $self->{-surface} = SDL::sdlpl::sdl_new_surface("",$f,$w,$h,$d,$r,$g,$b,$a); } bless $self,$class; return $self; } sub DESTROY { # Object Destructor my $self = shift; SDL::sdlpl::sdl_free_surface($self->{-surface}); # this function free the structure's memory } # # Surface fields ### READ-ONLY ### # sub flags { my $self = shift; return SDL::sdlpl::sdl_surface_flags($self->{-surface}); } sub palette { my $self = shift; return SDL::sdlpl::sdl_surface_palette($self->{-surface}); } sub bpp { my $self = shift; return SDL::sdlpl::sdl_surface_bits_per_pixel($self->{-surface}); } sub bytes_per_pixel { my $self = shift; return SDL::sdlpl::sdl_surface_bytes_per_pixel($self->{-surface}); } sub Rshift { my $self = shift; return SDL::sdlpl::sdl_surface_rshift($self->{-surface}); } sub Gshift { my $self = shift; return SDL::sdlpl::sdl_surface_gshift($self->{-surface}); } sub Bshift { my $self = shift; return SDL::sdlpl::sdl_surface_bshift($self->{-surface}); } sub Ashift { my $self = shift; return SDL::sdlpl::sdl_surface_ashift($self->{-surface}); } sub Rmask { my $self = shift; return SDL::sdlpl::sdl_surface_rmask($self->{-surface}); } sub Gmask { my $self = shift; return SDL::sdlpl::sdl_surface_gmask($self->{-surface}); } sub Bmask { my $self = shift; return SDL::sdlpl::sdl_surface_bmask($self->{-surface}); } sub Amask { my $self = shift; return SDL::sdlpl::sdl_surface_amask($self->{-surface}); } sub color_key { my $self = shift; return SDL::sdlpl::sdl_surface_colorkey($self->{-surface}); } sub alpha { my $self = shift; return SDL::sdlpl::sdl_surface_alpha($self->{-surface}); } sub width { my $self = shift; return SDL::sdlpl::sdl_surface_w($self->{-surface}); } sub height { my $self = shift; return SDL::sdlpl::sdl_surface_h($self->{-surface}); } sub clip_minx { my $self = shift; return SDL::sdlpl::sdl_surface_clip_minx($self->{-surface}); } sub clip_miny { my $self = shift; return SDL::sdlpl::sdl_surface_clip_miny($self->{-surface}); } sub clip_maxx { my $self = shift; return SDL::sdlpl::sdl_surface_clip_maxx($self->{-surface}); } sub clip_maxy { my $self = shift; return SDL::sdlpl::sdl_surface_clip_maxy($self->{-surface}); } sub pitch { my $self = shift; return SDL::sdlpl::sdl_surface_pitch($self->{-surface}); } sub pixels { my $self = shift; return SDL::sdlpl::sdl_surface_pixels($self->{-surface}); } # # Swiss-Army-Chainsaw pixel # # returns pixel value at (x,y) # if given an additional arg, will set pixel to (x,y,c) and will return the color it is set to. # FYI: RGB -> 16bit -> RGB does not always return the same number due to lossage # # *** Warining *** lock before using # sub pixel { my $self = shift; if ( 3 == @_ ) { # paranoid my ($x, $y, $color) = @_; # do a write return SDL::sdlpl::sdl_surface_pixel($self->{-surface},$x,$y,$color); # return for verification } else { # else my ($x,$y) = @_; # do just a read return SDL::sdlpl::sdl_surface_pixel($self->{-surface},$x,$y); # return the value } } sub fill { my $self = shift; my $rect = shift; my $color = shift; return SDL::sdlpl::sdl_fill_rect($self->{-surface},$rect->{-rect},$color); } # # Locking and Unlocking # sub lockp { my $self = shift; return SDL::sdlpl::sdl_surface_must_lock($self->{-surface}); } sub lock { my $self = shift; return SDL::sdlpl::sdl_surface_lock($self->{-surface}); } sub unlock { my $self = shift; return SDL::sdlpl::sdl_surface_unlock($self->{-surface}); } # # Updating bliting and the like # sub update { my $self = shift; my @irects = @_; my @rects; my $rect; for $rect (@irects) { push @rects, $rect->{-rect}; } SDL::sdlpl::sdl_update_rects ($self->{-surface}, @rects ); } sub flip { my $self = shift; SDL::sdlpl::sdl_flip($self->{-surface}); } sub blit { my $self = shift; my $srect = shift; my $dest = shift; my $drect = shift; return SDL::sdlpl::sdl_blit_surface($self->{-surface},$srect->{-rect},$dest->{-surface},$drect->{-rect}); } # # Palette mangling, trans, and alpha # sub set_colors { my $self = shift; my $start = shift; return SDL::sdlpl::sdl_set_colors($self->{-surface},$start,@_); } sub set_color_key { my $self = shift; my $flag = shift; my $pixel = shift; return SDL::sdlpl::sdl_set_color_key($self->{-surface},$flag,$pixel); } sub set_alpha { my $self = shift; my $flag = shift; my $alpha = shift; return SDL::sdlpl::sdl_set_alpha($self->{-surface},$flag,$alpha); } # # Clipping and format # sub clip { my $self = shift; my $top = shift; my $left = shift; my $bottom = shift; my $right = shift; SDL::sdlpl::sdl_set_clipping($self->{-surface},$top,$left,$bottom,$right); } sub display_format { my $self = shift; my $surface = $self;#new Surface(); #SDL::sdlpl::sdl_free_surface($surface->{-surface}); $surface->{-surface} = SDL::sdlpl::sdl_display_format($self->{-surface}); return $surface; } # Font support added Fri May 26 11:00:59 EDT 2000 sub print { my $self = shift; my $x = shift; my $y = shift; SDL::sdlpl::sdl_sfont_surface_print( $self->{-surface}, $x, $y, join('',@_)); } # Saving surface as BMP sub save_bmp { my $self = shift; my $filename = shift; return SDL::sdlpl::sdl_save_bmp( $self->{-surface}, $filename); } #vid info debug helper sub video_info { my $self = shift; return SDL::sdlpl::sdl_video_info (); } #helpers sub field_report { my $self=shift; my $hex_format="0x%08x"; my $report=""; $report.="Flags: ".sprintf($hex_format,$self->flags())."\n"; $report.="Pallete: ".$self->palette()."\n"; $report.="bits pix: ".$self->bpp()."\n"; $report.="bytes pix: ".$self->bytes_per_pixel()."\n"; $report.="Rshift: ".$self->Rshift()."\n"; $report.="Gshift: ".$self->Gshift()."\n"; $report.="Bshift: ".$self->Bshift()."\n"; $report.="Ashift: ".$self->Ashift()."\n"; $report.="Rmask: ".sprintf($hex_format,$self->Rmask())."\n"; $report.="Gmask: ".sprintf($hex_format,$self->Gmask())."\n"; $report.="Bmask: ".sprintf($hex_format,$self->Bmask())."\n"; $report.="Amask: ".sprintf($hex_format,$self->Amask())."\n"; $report.="ColourKey: ".$self->color_key()."\n"; $report.="Alpha: ".$self->alpha()."\n"; $report.="Width: ".$self->width()."\n"; $report.="Height: ".$self->height()."\n"; $report.="Clip_Min_x: ".$self->clip_minx()."\n"; $report.="Clip_Min_y: ".$self->clip_miny()."\n"; $report.="Clip_Max_x: ".$self->clip_maxx()."\n"; $report.="Clip_Max_y: ".$self->clip_maxy()."\n"; $report.="Pitch: ".$self->pitch()."\n"; return $report; } 1; __END__; =head1 NAME SDL::Surface - a SDL perl extension =head1 SYNOPSIS use SDL::Surface; $image = new SDL::Surface(-name=>"yomama.jpg"); =head1 DESCRIPTION SDL::Surface->new(-name=>"yomama.jpg"); will load an image named yomama.jpg, which works equally well with images of type bmp, ppm, pcx, gif, jpeg, and png. Optionally, if you would like a scratch surface to work with, you can createit using this function using the following syntax: SDL::Surface->new(-flags=>SDL_SWSURFACE,-width=>666,-height=>666,-depth=>8); which will produce a software suface, at 666x666x256 colors. To create an image using the default depth and flag state simply use: SDL::Surface->new(-width=>$my_w,-height=>$my_h); If you are on a big endian machine, or some really funky hardware you can set the RGBA bitmasks with the keys -Rmask, -Gmask, -Bmask, -Amask. For example to create a scratch surface on a big endian machine one would use: SDL::Surface->new(-width=>200,-height=>100, -Rmask=>0xff000000,-Gmask=>0x00ff0000, -Bmask=>0x0000ff00,-Amask=>0x000000ff); =head2 DESTROY When a surface is destroyed, perl will call SDL_FreeSurface() on it. Hence don't worry about freeing it yourself. If you must, then invoke the function SDL::sdlpl::sdl_free_surface($your_surface); =head2 Read-only Surface fields The SDL_Surface structure has many subfields. Most of these fields are accessible in read-only form. Most of these fields are useless to a perl hacker anyways, and are provided for a sense of completeness. $surface->flags() This field returns the flags which are applicable to the current surface. The possible values for the flags are: SDL_ANYFORMAT SDL_SWSURFACE SDL_HWSURFACE SDL_HWPALETTE SDL_DOUBLEBUF SDL_FULLSCREEN $surface->palette() This field returns a pointer to the SDL_Palette structure for this image if it is an 8 bit image, or NULL if the image is 16, 24, or 32 bit. cf. Palette.pm NB: this should not be passed to any Palette method. what you need there is to create a new Palette object, passing it the image as in: my $pal = new SDL::Palette $image; cf Palette.pm for details. $surface->bpp() This field returns the bits per pixel, aka depth, of the surface. $surface->bytes_per_pixel() This field returns the bytes per pixel, this should in all cases be the same as bpp/8. Technically, it reads that field of the SDL_PixelFormat structure for the surface. $surface->Rshift(); $surface->Gshift(); ... These functions return the Rshift, Gshift, Bshift, and Ashift respectively for the surface. To be perfectly honest, these are not all that useful from perl, but are provided for future expansion. $surface->Rmask(), $surface->Gmask() ... Like Rshift & friends, Rmask, Gmask, Bmask, and Amask return the current byte masks for each component of RGBA surfaces. They are provided for future expansion. $surface->color_key(); This returns the pixel value which was set using $surface->set_color_key(flag,pixel); This is the transparent color for the image. $surface->alpha(); This returns the alpha value of the surface, usually set by $surface->set_alpha(flag,alpha); Alpha values are not applicable to palettized images. $surface->width(); This returns the width of the surface. $surface->height(); This returns the height of the surface. $surface->clip_minx ... clip_miny ... clip_maxx ... clip_maxy clip_minx, clip_miny, clip_maxx, and clip_maxy return the current clipping values for the surface. $surface->pitch(); This returns the pitch of the image, aka the width of a single row of pixels in bytes. This should be the same as $surface->width()*$surface->bytes_per_pixel(). It is provided for future expansion. =head2 Poking Pixels The swiss-army-chainsaw of pixel manipulation, the method $surface->pixel(x,y,color); can be used to read or set the value of a pixel at the point (x,y). If no color value is passed, it will simply read the value at that point. If a color is passed, it will set the point to that value and then return the value to which it was actually set. For the speed conscious, this function is SLOW. It is not designed for line drawing or other large-scale projects. It is my intention to provide a generic drawing system in the future, and additional low level memory tools, but in the mean time, if you must this cudgel. So just to reiterate: $surface->pixel(12,13); # returns the pixel at (12,13) $surface->pixel(12,13,0xffff); # sets that pixel to 0xffff =head2 Filling areas To fill a larger volume than a single pixel the method: $surface->fill($rect,color); will fill a rectangle with the value of color. With intelligent use of Rect objects one can draw buttons and the like. It is also useful in clearing the screen. =head2 Locking, Unlocking, and the like... When writing to certain surfaces, notably some SDL_HWSURFACE flaged surfaces, it is necessary to lock the surface before twiddling its bits. To tell if you must do so it is wise to call the function: $suface->lockp(); If it returns non-zero you should then call: $surface->lock(); and after your write call: $surface->unlock(); and everything will be well. Locking and unlocking a surface which doesn't require locking only wastes time, so if you have time to spare, you could just do so by default. =head2 Bliting & Updating To copy image data from one surface to another, the SDL provides a collection of functions. $surface->update(rect,...); will update any number of Rects of the screen, where as: $surface->flip(); will update the entire screen. $surface->blit($srect,$dest,$drect); will copy the contents contained by $srect of the surface $surface into area specified bye $drect of the surface $dest. Please see the documents for Rect.pm for the creation of $srect and $drect. =head2 So may Colors in the world ... To set the colors in a palettized image, the method: $surface->set_colors($start,...) is provided. It takes 1 or more Color objects, and sets the consecutive palette entries starting at entry $start. Hence to set the first ten palette entires one would use: $my_surface->set_colors(0,$color1,$color2,...$color10); where $color1 ... $color10 were created with Color->new(r,g,b); see Color.pm for details. Transperancy and alpha levels for an image can also be set using the methods: $surface->set_color_key(flag,pixel); and $surface->set_alpha(flag,alpha); where the flags for set_color_key are: SDL_SRCCOLORKEY and SDL_RLEACCEL, and the flags for set_alpha are: SDL_SRCALPHA and SDL_MULACCEL. =head2 Clipping To set the clipping rectangle for a source surface the method $surface->clip(top,left,bottom,right); will prevent accidental bliting of material outside of this rectangle. =head2 Format Conversion Occasionally it will be necessary to manipulate images in a format other than that used by the display, to manage the conversion the method: $surface->display_format(); will produce a new surface in the format used for display. =head2 Fonts & Printing As of version 1.01, SDLpl now supports SFont style fonts. Before printing, one must create and use a new font as specified in SDL::Font. For example to load a font stored in Ionic.png one would use: $font = new Font "Ionic.png"; $font->use; Then to print a string to (10,13) on a surface one would use the print method of the surface: $surface->print(10,13,"Hello World"); =head1 AUTHOR David J. Goehrig =head1 SEE ALSO perl(1) SDL::Rect(3) SDL::Font(3). =cut