Surface tutorial

Introduction
Surfaces were first implemented in Game Maker 6.1. The surface functions allow the user to use the standard drawing functions to draw on a canvas, or a surface, once and then perform operations on the surface such as saving it to a file, drawing it on the screen, applying transformations on it, or copying it to another surface.

Benefits
The benefits of using surfaces are most apparent when drawing complex figures which require many calls to various drawing functions. Instead of calling many functions each time the draw event is fired, one can simply draw to a surface and then draw this surface to the screen each time the screen is redrawn. This means that even if you draw thousands of lines on the surface, only a single function call is needed to redraw the surface each step which can tremendously reduce overhead.

Concept
To understand surfaces one must understand how the Game Maker runner draws on the screen. Each time a drawing function is called (normally in the draw event) it performs its operations on a surface known as the 'target' surface. By default this surface is a special surface which is created by the runner when the game loads. At the end of each step this special surface is automatically drawn to the screen and erased. When you opt to draw on a user-created surface you are simply changing the target surface. However, this new target surface is not the default surface and therefore is not automatically drawn to the screen or refreshed at the end of each step.

First Attempt
Ok! So let's try out these functions that we have just learned in a piece of code, and see what happens! We will create an object called o_surfaces and put this code in its Create Event.

surface=surface_create(50,100); //Creates the surface draw_text(5,5,"This text is drawn in the create event."); /*The code above attempts to draw text in the create event!*/

Explanation of First Attempt
The code above creates a surface called surface and attempts to use it to draw text in the create event, and therefore take advantage of the surfaces by using a drawing action once to draw on a surface on the screen.

Problem
But after you place the object in the room and run the game, you will notice that no text is drawn! Why is that? You wonder!

Reason
The answer is simple! Game Maker doesn't understand automatically when to draw on a certain surface and when you need to draw on the main room, so you need to use certain functions that will switch between drawing on surfaces and the main room, and switch between making the drawings on certain surfaces between each other.

Code explanation will help:

Second Attempt
Ok, so now, we will do another attempt to draw on the screen using surfaces:

surface=surface_create(50,100); //Creates the surface surface_set_target(surface); //Sets drawing surface target draw_text(5,5,"This text is drawn in the create event."); /*The code above attempts to draw text in the create event!*/ surface_reset_target; /*Resets the drawing target. The drawing target is not set as the main screen in order for the game to continue functioning properly without corruption.*/

Problem
Go ahead and test this code, you may think it will work. But it still wouldn't! Do you have any idea why?

Reason
It's simple: the test is saved in the surface, and it is in the memory, but it is not drawn to the game's main screen! It is true that I said you don't have to use the draw functions constantly to draw all the drawings and lines, but however, you do need to draw the surface. So when you draw a 100 lines in the surface, you only need to execute one each step (the surface drawing function), not zero. Below is the code explanation for the surface drawing functions. They have been copied from the help field, then edited to make the functions clearer.


 * (id,x,y) Draws the surface with the given id in the position (x,y) without any colors or alpha blending.
 * (id,x,y,w,h) Draws the surface with the given id stretched to the width of w and h, in the position (x,y).
 * (id,x,y) Draws the surface with the given id tiled so that it fills the entire room. It starts the tile in the position (x,y).
 * (id,left,top,width,height,x,y) Draws the indicated part of the surface with the given id that is drawn at the position (x,y). The part of the surface starts and the position (left,top) with the width width and the height.
 * (id,x,y,xscale,yscale,rot,color,alpha) Draws the surface with the given id in the position (x,y) scaled by xscale and yscale (1= no scaling) and rotated using the value rot (0=no rotation), and with blending color (use c_white for no blending) and the transparency alpha value from 0 to 1, whereas 0 is transparent completely.
 * (id,x,y,w,h,color,alpha) Draws the surface with the given id stretched to the size of (w,h) in the position (x,y). Color is the blending color (c_white=no color blending) and alpha indicates the transparency setting from 0 to 1 whereas 0=transparent.
 * (id,x,y,xscale,yscale,color,alpha) Draws the surface with the given id tiled so that it fills the entire room and starts in the position (x,y), but now with scale factors (xscale,yscale) and a color blending (color) and transparency setting (alpha) from 0 to 1 whereas 0 is transparent.
 * (id,left,top,width,height,x,y,xscale,yscale,color,alpha) Draws the indicated part of the surface with the given id that starts on the position (left,top) of the surface, with the width and height (width,height), with its origin at position (x,y) but now with scale factors (xscale,yscale) and a color blending (color) and transparency setting (alpha) from 0 to 1 whereas 0 is transparent.
 * (id,left,top,width,height,x,y,xscale,yscale,rot,c1,c2,c3,c4,alpha) The most general drawing function. It draws the indicated part of the surface with the given id, that starts on the position (left,top) in the surface, with the width and height of (width,height), with its origin at position (x,y) but now with scale factors (xscale,yscale), a rotation angle (rot), a color for each of the four vertices (top-left, top-right, bottom-right, and bottom-left) (c1,c2,c2,c4 respectively), and an alpha transparency value (alpha) from 0 to 1 whereas 0 is transparent.

Third Attempt
So in order to make the drawing on surfaces function work properly, you need to draw the surface in the draw event, all the other code that you have made in the create event is correct and must stay the same. Add the following code to the Draw Event.

draw_surface(surface,0,0); //Draws the surface

Result
Now you can clearly see that the text is drawn, even though only the surface drawing function is called! Depending on your system and font-size settings, you may see that a part of the text is not drawn, that is because your surface width is 50, and there might not be room for all the text! This, ladies and gentlemen is the magic of surfaces!

Now let's move to the more-complex part of the surfaces, which is the once dealing with files and saving.

Example
Example is adding this to the Create Event of o_surfaces:

surface_save_part(surface,working_directory+"/Surface Part.bmp",7,10,25,15); //Saves a part of it.

This saves the surface as a .bmp wherever the game is located (This means if you haven't saved the game yet, the surface will be saved as a temporary file and deleted after the game is closed).

More Functions
Now, all we have to learn which is not very complex is getting information about the pixels of the surface, and copying a surface's contents into another surface!


 * (id,x,y) This function gets the color of the pixel in the position (x,y) in the surface with the given id.
 * (destination,x,y,source) This function copies the whole surface (source) to the new surface (destination). The (source) copied will be pasted in the position (x,y) of the new surface (destination). The copying occurs without doing any blending.
 * (destination,x,y,source,xs,ys,ws,hs) This function copies a part of the surface (source) to the new surface (destination). The (source) copied will be pasted in the position (x,y) of the new surface (destination). The part of the (source) to be copied will start on position (xs,ys) of the source and have the width (ws) and height (hs). The copying occurs without doing any blending.

Please keep the following information in mind (taken from Help File):

"(Help File) Note that there are no functions to copy part of the screen to a surface. (This is impossible due to possible format differences between the screen and the surfaces.) If this is required you must set a surface as render target and next draw the room. You can then use the surface copying routines to get parts of it.

Note that you can also create sprites and backgrounds from surfaces. See the section on changing resources for more information.

Some care must be taken when using these functions. In particular please notice the following:


 * You should never change the drawing target while you are actually drawing on the screen, that is, never use it in drawing events. This will cause serious problems with the projection and viewport.
 * Surfaces do not work correctly with 3D mode. You can use them while not in 3DD mode (by calling d3d_end before using them, but once you start 3D mode again the surfaces will be destroyed.
 * For reasons of speed, the surface is maintained in video memory only. As a result, you might loose the surface when e.g. the screen resolution changes or the screensaver pops up."

Limitations
Surfaces will often not work correctly if they exceed certain dimensions. For technical reasons, graphics cards have a limit on the size a single texture (a surface) can be. In very old cards this can be as little as 1024x1024. 2048x2048 is the most common limit, with 4096x4096 and even 8192x8192 being possible on very modern cards. As such, it is recommended that you limit your surfaces to 2048x2048 or lower in size.

The same limitations apply to backgrounds.