SDL Coordinates and Bliting
October 25th, 2007 by Tim Jones Leave a reply »
Using the first tutorial as our base, we’ll delve more into the world of SDL surfaces. As I attempted to explain in the last lesson, SDL Surfaces are basically images stored in memory. Imagine we have a blank 320×240 pixel surface. Illustrating the SDL coordinate system, we have something like this:
把第一節的教程作為我們的基礎,我們繼續來深入探究SDL surface的世界。就如同我上個教程裡所描述的一樣, SDL surface基本上都是儲存在記憶體裡的一些圖片。假設我們有一個320x240的surface。我們可以用下面的圖來示範他的座標系統:
This coordinate system is quite different than the normal one you are familiar with. Notice how the Y coordinate increases going down, and the X coordinate increases going right. Understanding the SDL coordinate system is important in order to properly draw images on the screen. 這個座標系統和我們平時所熟悉的座標系統很不一樣。注意Y軸是從上往下增長,而X軸是從左往右增長。熟悉SDL的座標系統,對於把影像畫在合適的位置是很重要的。 Since we already have the main surface (Surf_Display) setup and ready, we are going to need a way to draw images on it. This process is called Blitting, where we basically transfer one image onto another. But before we can do that, we have to have a way to load these images into memory. SDL offers a simple function to do this called SDL_LoadBMP. Some pseudocode might look like this: 之前我們已經設定好了主皮膚(Surf_Display),現在我們只需要解決怎樣在它上面畫圖片就可以了。這個過程叫Blitting,我們只要要簡單地把一個影像轉換成另一個就可以了。但是在轉換之前,我們有很多種方法來吧影像裝載到記憶體裡。SLD提供了一個簡單的函式——SDL_LoadBMP,來實現這個功能。一個簡單的示例程式碼如下: SDL_Surface* Surf_Temp; if((Surf_Temp = SDL_LoadBMP("mypicture.bmp")) == NULL) { //Error! } It’s rather simple, SDL_LoadBMP takes a single argument, the file you want to load, and returns a surface. If the functions returns NULL, either the file is not found, corrupted, or some other error. Unfortunately, for the sake of effeciency, this method is not enough. Often the image loaded will be in a different pixel format than the display. Thus, when we draw an image to the display we can incurr performance loss, lose image colors, and the like. But, thanks to SDL, it offers a quick solution around this, SDL_DisplayFormat. This function takes a surface already loaded, and returns a new surface using the same format as the display. 這個函式很簡單,它只有一個引數,就是你想載入的檔案的路徑,然後它會返回一個surface。如果載入的檔案不存在,衝突了,或者出現其他錯誤了,返回的值都是NULL。不幸的是,如果要考慮到效率的話,僅僅使用這個方法是不足夠的。我們會經常碰到這樣的情況,影像的畫素格式和我們的顯示器是不一樣的。因此,如果我們我們就這樣把載入的影像畫到螢幕上趨勢,,我們會有質量的損失,丟失畫素,或者其他類似的問題。不過SDL提供了一個很有效的方法來解決這個問題,使用SDL_DisplayFormat函式。把已經載入到記憶體的surface傳遞給它,它可以返回一個新的和螢幕格式一致的surface。
Let’s take this process and throw it into a reusable class. Use SDL Tutorial 1 as the basis for you code, and add the following two new files: CSurface.h, CSurface.cpp. Open up CSurface.h and add the following: 讓我們來實現這這個過程,然後讓它成為一個可用的類。用上一節的程式碼做為基礎,然後新增以下兩個檔案:CSurface.h,CSurface.cpp。開啟CSurface.h檔案,然後新增以下內容:
ifndef _CSURFACE_H_
#define _CSURFACE_H_
include
class CSurface { public: CSurface(); public: static SDL_Surface* OnLoad(char* File); };
endif
We’ve created a simple static function, OnLoad, that will load a surface for us. Now open CSurface.cpp: 在這裡,我們宣告瞭一個簡單的靜態函式,OnLoad,它將負責裝載一個surface。現在打卡CSurface.cpp:
include "CSurface.h"
CSurface::CSurface() { } SDL_Surface* CSurface::OnLoad(char* File) { SDL_Surface* Surf_Temp = NULL; SDL_Surface* Surf_Return = NULL; if((Surf_Temp = SDL_LoadBMP(File)) == NULL) { return NULL; } Surf_Return = SDL_DisplayFormat(Surf_Temp); SDL_FreeSurface(Surf_Temp); return Surf_Return; } There are a couple of important things to note here. Firstly, always remember that when you make a pointer to set it to NULL, or 0. Many problems can come along later if you fail to do this. Secondly, notice how SDL_DisplayFormat returns a new Surface, and doesn’t overwrite the original. This important to remember because since it creates a new surface, we have to free the old one. Otherwise, we have a surface floating around in memory. 這裡還有幾個問題需要注意。首先,始終記住一點,當你使用一個指標時,首先要把它設定成NULL,或者0。如果你不這樣做,很容易就會出問題的。其次,注意SDL_DisplayFormat函式返回了一個新的surface,而不是重新使用之前的surface。因此我們需要釋放之前的那個surface,以防止記憶體洩露。 Now that we have a way of loading surfaces into memory, we are also going to need a way to draw them onto other surfaces. Just like SDL offers a function to load images, it also offers a function to draw (blit) images: SDL_BlitSurface. Unfortunately, this function is not as easy to use as the SDL_LoadBMP, but nonetheless, it’s simple enough. Open back up CSurface.h and add the following function prototype: 現在,我們終於可以把surface裝載到記憶體裡了,現在我們只需要想辦法把它畫到其他的surface上去了。同樣,SDL也提供了實現這個功能的方法,SDL_BlitSurface。不過,這個方法並沒有SDL_LoadBMP那麼簡單,但是也很簡單。開啟CSurface.h檔案,並把下面的函式原型加入:
ifndef _CSURFACE_H_
#define _CSURFACE_H_
include
class CSurface { public: CSurface(); public: static SDL_Surface* OnLoad(char* File); static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y); };
endif
Now, open back up CSurface.cpp, and add the following: 現在,重新開啟CSurface.cpp,新增以下內容:
include "CSurface.h"
CSurface::CSurface() { } SDL_Surface* CSurface::OnLoad(char* File) { SDL_Surface* Surf_Temp = NULL; SDL_Surface* Surf_Return = NULL; if((Surf_Temp = SDL_LoadBMP(File)) == NULL) { return NULL; } Surf_Return = SDL_DisplayFormat(Surf_Temp); SDL_FreeSurface(Surf_Temp); return Surf_Return; } bool CSurface::OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y) { if(Surf_Dest == NULL || Surf_Src == NULL) { return false; } SDL_Rect DestR; DestR.x = X; DestR.y = Y; SDL_BlitSurface(Surf_Src, NULL, Surf_Dest, &DestR); return true; } First of all, look at the arguments being passed to the OnDraw function. We have two surfaces, and two int variables. The first surface is the destination surface, or the surface we are going to draw on. The second surface is the source surface, or the surface we going to use to draw on another surface. Basically, we are putting Surf_Src on top of Surf_Dest. The X, Y variables is the position on the Surf_Dest where we are drawing this surface to. 首先,看看傳給OnDraw函式的引數,我們定義了兩個surface以及兩個int型別的變數。第一個surface是我們的將要在上面畫東西的;第二個surface,是我們要拿來畫到另一個surface上的。基本上,我們只是要把Surf_Src放到Surf_Dest上九可以了。X,Y變數是表示要在Surf_Dest的哪個位置放置Surf_Src。 The start of the function makes sure we have valid surfaces, if we don’t, return false. Next, we find SDL_Rect. This is a SDL structure that basically has four members: x, y, w, h. This, of course, creates the dimensions for a rectangle. We are only worried about where we are drawing to, not the size. So we assign X, Y coordinates to the destination surface. If you are wondering what NULL is within SDL_BlitSurface, it’s another parameter for a SDL_Rect. We’ll get to this later on in this lesson. Lastly, we actualy call the function to draw the image, and then return true. Now, to make sure all of this works, let’s create a test surface. Open up CApp.h, and create a new surface, and include the new created CSurface.h:
ifndef _CAPP_H_
#define _CAPP_H_
include
include "CSurface.h"
class CApp { private: bool Running; SDL_Surface* Surf_Display; SDL_Surface* Surf_Test; public: CApp(); int OnExecute(); public: bool OnInit(); void OnEvent(SDL_Event* Event); void OnLoop(); void OnRender(); void OnCleanup(); };
endif
Also, initialize the surface to NULL in the constructor: CApp::CApp() { Surf_Test = NULL; Surf_Display = NULL; Running = true; } And, remember to cleanup:
include "CApp.h"
void CApp::OnCleanup() { SDL_FreeSurface(Surf_Test); SDL_FreeSurface(Surf_Display); SDL_Quit(); } Now, lets actually load an image. Open up CApp_OnInit.cpp, and add the code to load a surface:
include "CApp.h"
bool CApp::OnInit() { if(SDL_Init(SDL_INIT_EVERYTHING) < 0) { return false; } if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) { return false; } if((Surf_Test = CSurface::OnLoad("myimage.bmp")) == NULL) { return false; } return true; } Be sure to replace “myimage.bmp” with an actual bitmap image you have. If you don’t have one, open mspaint and draw something real quick, and save it in the same folder where your executable goes. Now that we have the image loaded, lets actually draw it. Open up CApp_OnRender.cpp and add the following:
include "CApp.h"
void CApp::OnRender() { CSurface::OnDraw(Surf_Display, Surf_Test, 0, 0); SDL_Flip(Surf_Display); } Notice a new function here SDL_Flip. This basically refreshes the buffer and displays Surf_Display onto the screen. This is called double buffering. It’s the process of drawing everything into memory, and then finally drawing everything to the screen. If we didn’t do this, we would have images flickering on the screen. Remember the SDL_DOUBLEBUF flag? This is what turns double buffering on. Compile your code, and make sure everything works correctly. You should see your image on the top left hand corner of the screen. If so, congratulations, you are one step closer to an actual game. If not, make sure you have the myimage.bmp in the same folder as your executable. Also insure it’s a valid bitmap file.
Now lets take this process a little bit further. While it’s nice and all to draw images to the screen, often we’ll need to draw only parts of an image. Take, for example, a tileset:
Though this is one single image, we only want to draw a part of it. Open back up CSurface.h, and add the following code:
ifndef _CSURFACE_H_
#define _CSURFACE_H_
include
class CSurface { public: CSurface(); public: static SDL_Surface* OnLoad(char* File); static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y); static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H); };
endif
Open back up CSurface.cpp, and add the following function: bool CSurface::OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H) { if(Surf_Dest == NULL || Surf_Src == NULL) { return false; } SDL_Rect DestR; DestR.x = X; DestR.y = Y; SDL_Rect SrcR; SrcR.x = X2; SrcR.y = Y2; SrcR.w = W; SrcR.h = H; SDL_BlitSurface(Surf_Src, &SrcR, Surf_Dest, &DestR); return true; } Notice that it’s basically the same function as our first one, except we’ve added another SDL_Rect. This source rect allows use to specify what pixels from the source to copy over to the destination. If we specified 0, 0, 50, 50 as parameters for X2…H, it would only draw upper left part of the surface (a 50×50 square).
Lets also test this new function out, open back up CApp_OnRender.cpp, and add the following:
include "CApp.h"
void CApp::OnRender() { CSurface::OnDraw(Surf_Display, Surf_Test, 0, 0); CSurface::OnDraw(Surf_Display, Surf_Test, 100, 100, 0, 0, 50, 50); SDL_Flip(Surf_Display); } You should notice that your image is drawn at 100, 100 and only part of it is being displayed. You should take special care understanding how these functions work, and how the SDL coordinate system is setup, you will be using this quite a bit. Jump on over to the next SDL tutorial, where we’ll look more at SDL events, and how to make that process a whole lot simpler.
相關文章
- SDL簡介
- Mac: ‘SDL2/SDL_events.h‘ file not found解決方案及demo示例Mac
- SDL在win上安裝
- SDL suite 5.5 (11千字)UI
- SDL Guide 中文譯版(一) (轉)GUIIDE
- SDL Guide 中文譯版(二) (轉)GUIIDE
- SDL Guide 中文譯版(四) (轉)GUIIDE
- 使用SDL2中SDL_CreateWindow()函式時報錯跳進wincore.cpp(wntdll.pbd not load)函式
- 七、FFmpeg 4.0.2 + SDL2 播放音訊音訊
- SDL程式設計入門(26)運動程式設計
- SDL3 入門(5):紋理渲染
- SDL程式設計入門(25)限制幀率程式設計
- SDL Guide 中文譯版(三上) (轉)GUIIDE
- SDL Guide 中文譯版(三下) (轉)GUIIDE
- 某物秋招SDL安全工程師面試工程師面試
- SDL_app:emulator.exe應用程式錯誤APP
- SDL3 入門(2):第一個視窗
- SDL3 入門(4):選擇圖形引擎
- 「SDL第六篇」孫悟空與多執行緒執行緒
- SDL程式設計入門(23)高階定時器程式設計定時器
- FFMPEG+SDL簡單影片播放器——影片快進播放器
- SDL程式設計入門(29)圓形碰撞檢測程式設計
- SDL3 入門(3):三角形
- Qt基於SDL庫簡單實現YUV影片播放QT
- SDL程式設計入門(28)每畫素碰撞檢測程式設計
- FFmpeg+SDL2實現簡易音視訊同步播放器播放器
- FFmpeg開發筆記(九):ffmpeg解碼rtsp流並使用SDL同步播放筆記
- 基於ffmpeg+SDL的加密視訊播放器的開發(三)加密播放器
- 從SDL到DevSecOps:始終貫穿開發生命週期的安全dev
- 宜信SDL實踐:產品經理如何驅動產品安全建設
- FFmpeg開發筆記(八):ffmpeg解碼音訊並使用SDL同步音訊播放筆記音訊
- SDL:英國76%的使用者不滿意通過WiFi跟蹤手機資料WiFi
- 一步一步搭建基於ffmpeg和sdl2的流媒體播放器播放器