Pull to refresh

2D рендеринг в SDL

Reading time7 min
Views3.9K
imageДолго я не хотел писать данную статью — думал как подавать материал. Но, видно сегодня удачно сложились звёзды и статье про SDL быть. Хотя, это всего лишь черновой вариант. В будущем данную статью разобью на несколько отдельных — материала и кода достаточно.

Для тех, кто ещё не знаком с этой старой, но очень хорошей кроссплатформенной графической библиотекой, я бы советовал прочитать этот пост.
Скачать библиотеку можно с официального сайта, думаю это не составит большого труда. Я не буду вдаваться в подробности, так как статься получится просто необъятной, их можно найти тут.

Сегодня мы напишем с нуля программу, которая зальёт экран тремя цветами.

Итак приступим:
У нас будет 3 вспомогательных класса и основная часть программы, которая будет «зажигать» пиксели на экране любыми цветами. Ещё раз повторю, что тут будут описаны только самые основы, которые должны будут заинтересовать читателя.

Прежде всего, требуется объявить класс, который будет непосредственно загружать библиотеку и оперировать всеми её функциями. Такими как (раз)блокировка экрана, работа с пикселями и поверхностью на которой мы будем «рисовать». Основная часть кода, находящаяся в этом классе есть в справочных файлах нашей библиотеки. Хотя как он работает и почему зажигаются пиксели всё же было бы полезно узнать мне кажется, но это выходит за рамки статьи.

SDL_Interface.cpp
  1. #include <SDL.h>
  2. #include "Vector_2D.cpp"
  3. #include "Color.h"
  4.  
  5. class SDL_Interface{
  6.   public:
  7.     SDL_Interface(int, int, int);
  8.     ~SDL_Interface();
  9.     void putpixel(int x, int y, const Color&);
  10.     void putpixel(const Vector_2D&, const Color&);
  11.     int const h(); /*Высота экрана экрана*/
  12.     int const w(); /*Ширина экрана*/
  13.     bool const key_wait(int);
  14.  
  15.     void const screen_unlock();
  16.     int const screen_lock();
  17.     void const screen_update(Sint32, Sint32, Sint32, Sint32);
  18.   protected:
  19.     SDL_Surface *screen;
  20.     SDL_Event event;
  21. };
  22.  
  23. SDL_Interface::SDL_Interface(int width, int height, int color_depth) {
  24.  
  25.   /* Инициализация SDL */
  26.   if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
  27.     fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
  28.     exit(1);
  29.   }
  30.  
  31.   screen = SDL_SetVideoMode(width, height, color_depth, SDL_SWSURFACE);
  32.   if ( screen == NULL ) {
  33.     fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError());
  34.     exit(1);
  35.   }
  36. }
  37.  
  38. SDL_Interface::~SDL_Interface(){
  39.   SDL_Quit();
  40. }
  41.  
  42. int const SDL_Interface::w(){
  43.   return screen->w;
  44. }
  45.  
  46. int const SDL_Interface::h(){
  47.   return screen->h;
  48. }
  49.  
  50. int const SDL_Interface::screen_lock(){
  51.   /* Блокировка экрана для доступа к пикселям */
  52.   if ( SDL_MUSTLOCK(screen) ) {
  53.     if ( SDL_LockSurface(screen) < 0 ) {
  54.       fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
  55.       return 0;
  56.     }
  57.   }
  58.   return 0;
  59. }
  60.  
  61. void const SDL_Interface::screen_unlock(){
  62.   if ( SDL_MUSTLOCK(screen) ) {
  63.     SDL_UnlockSurface(screen);
  64.   }
  65. }
  66.  
  67. void const SDL_Interface::screen_update(Sint32 x, Sint32 y, Sint32 w, Sint32 h){
  68.   SDL_UpdateRect(screen, x, y, w, h);
  69. }
  70.  
  71. bool const SDL_Interface::key_wait(int key_id){
  72.     while ( SDL_WaitEvent(&event) >= 0 ) {
  73.         Uint8 *keys;  
  74.         keys = SDL_GetKeyState(NULL);
  75.     if ( keys[key_id] == SDL_PRESSED ) {
  76.             return true;
  77.         }
  78.         if (event.type == SDL_QUIT){
  79.             exit(0);
  80.         }
  81.      }
  82.     return false;
  83. }
  84.  
  85. void SDL_Interface::putpixel(const Vector_2D& vector_2d, const Color& color)
  86. {
  87.   putpixel(vector_2d.x_coord, vector_2d.y_coord, color);
  88. }
  89.  
  90. /*Вставка пиксела в заданные координаты экрана с указанием цвета*/
  91. void SDL_Interface::putpixel(int x, int y, const Color& color)
  92. {
  93.   Uint32 pixel = SDL_MapRGB(screen->format, color.R, color.G, color.B);
  94.   SDL_Surface *surface = screen;
  95.   int bpp = surface->format->BytesPerPixel;
  96.   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
  97.  
  98.   switch(bpp) {
  99.   case 1:
  100.     *p = pixel;
  101.     break;
  102.  
  103.   case 2:
  104.     *(Uint16 *)p = pixel;
  105.     break;
  106.  
  107.   case 3:
  108.     if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
  109.       p[0] = (pixel >> 16) & 0xff;
  110.       p[1] = (pixel >> 8) & 0xff;
  111.       p[2] = pixel & 0xff;
  112.     } else {
  113.       p[0] = pixel & 0xff;
  114.       p[1] = (pixel >> 8) & 0xff;
  115.       p[2] = (pixel >> 16) & 0xff;
  116.     }
  117.     break;
  118.  
  119.   case 4:
  120.     *(Uint32 *)p = pixel;
  121.     break;
  122.   }
  123. }
* This source code was highlighted with Source Code Highlighter.


Класс для работы с цветом будет тривиальным и совсем маленьким, в него конечно можно добавить арифметические операции с цветами, свои палитры цветов, но мы этого делать не будем.

Color.h
  1. class Color{
  2.     public:
  3.         Color(Uint8 red, Uint8 green, Uint8 blue) : R(red), G(green), B(blue) {};
  4.         double R, G, B;
  5. };
* This source code was highlighted with Source Code Highlighter.


И вот, постепенно, мы подбираемся к самому интересному, классу, который будет руководить нашими точками. Как мы знаем, у каждого пикселя на экране есть две координаты, вот их мы и будем передавать в конструктор класса. Условимся называть конкретную точку экрана вектором, и определим арифметические операции между векторами (стоит отметить, что все эти операции выполняются покоординатно). Это делается для того, чтобы в будущем не вызывали проблем геометрические операции над векторами.

Vector_2D.cpp
  1. class Vector_2D{
  2. public:
  3.     Vector_2D(void) : x_coord(0), y_coord(0) {};
  4.     Vector_2D(int X, int Y);
  5.     Vector_2D operator- (const Vector_2D&) const;
  6.     Vector_2D operator+ (const Vector_2D&) const;
  7.     Vector_2D operator* (const Vector_2D&) const;
  8.  
  9.     int x_coord, y_coord;
  10. };
  11.  
  12. Vector_2D::Vector_2D(int X, int Y){
  13.     x_coord = X;
  14.     y_coord = Y;
  15. }
  16.  
  17. Vector_2D Vector_2D::operator -(const Vector_2D &rhs) const
  18. {
  19.     Vector_2D Point;
  20.     Point.x_coord = (this->x_coord - rhs.x_coord);
  21.     Point.y_coord = (this->y_coord - rhs.y_coord);
  22.  
  23.     return Point;
  24. }
  25.  
  26. Vector_2D Vector_2D::operator +(const Vector_2D &rhs) const
  27. {
  28.     Vector_2D Point;
  29.     Point.x_coord = (this->x_coord + rhs.x_coord);
  30.     Point.y_coord = (this->y_coord + rhs.y_coord);
  31.  
  32.     return Point;
  33. }
  34.  
  35. Vector_2D Vector_2D::operator *(const Vector_2D &rhs) const
  36. {
  37.     Vector_2D Point;
  38.     Point.x_coord = (this->x_coord * rhs.x_coord);
  39.     Point.y_coord = (this->y_coord * rhs.y_coord);
  40.  
  41.     return Point;
  42. }
* This source code was highlighted with Source Code Highlighter.


А вот и самая главная часть нашей программы. Тут заключается вся логика рисования и управления.
test.cpp
  1. #include <iostream>
  2. #include "SDL_Interface.cpp"
  3.  
  4. int main(int argc, char** argv) {
  5.  
  6.   //Объявляем поверхность на которой мы будем рисовать с размерами экрана и глубиной цвета
  7.   SDL_Interface *mySDL = new SDL_Interface(800, 600, 32);
  8.  
  9.     //Определяем цвета
  10.     Color blue   (0x00, 0x00, 0xff);
  11.     Color red    (0xff, 0x00, 0x00);
  12.     Color green   (0x00, 0xff, 0x00);
  13.     
  14.     mySDL->screen_lock(); //Блокируем экран перед началом рисования
  15.  
  16.   /*Рисуем путём заливания экрана тремя цветами*/
  17.   for (int x=0; x<mySDL->w(); x++){
  18.         for(int y=0; y<mySDL->h(); y++){
  19.             if(y < (mySDL->h())/3)
  20.                 mySDL->putpixel(Vector_2D(x,y), green);
  21.             else if(y < ((mySDL->h())/3)*2)
  22.                 mySDL->putpixel(Vector_2D(x,y), red);
  23.             else
  24.                 mySDL->putpixel(Vector_2D(x,y), blue);
  25.         }
  26.     }
  27.  
  28.   mySDL->screen_unlock();
  29.   mySDL->screen_update(0,0,0,0);
  30.  
  31.   if (mySDL->key_wait(SDLK_ESCAPE)) exit(0); //Выходим если ESC
  32.     
  33.   return 0;
  34. }
* This source code was highlighted with Source Code Highlighter.


В заключении скажу что меня эта библиотека покорила простором для действий. Да, многое из того что приходится писать самому уже давно реализовано, но для начинающих и для тех кто хочет просто поиграться или создать собственную игру «Жизнь» на c++ это будет отличный выбор!
Tags:
Hubs:
+4
Comments12

Articles