20141013:TP:C:GIT:SDL:EN

De wiki-prog
Révision de 13 octobre 2014 à 10:31 par Slashvar (discuter | contributions) (Cloning a repository)

(diff) ← Version précédente | Voir la version courante (diff) | Version suivante → (diff)
Aller à : navigation, rechercher


Introduction

The purpose of this session is to provide a simple boostrap on image manipulations using the SDL library, it's using SDL 1.2 version (SDL2 is not very different.) It's also the occasion to start your GIT repository for the project.

GIT set-up

GIT provides mechanim to keep track of versions of files and synchronization between copies of the same source tree. Basic operations you need to know are how to submit (commit) your changes, how to push your changes to a server and how to pull new version from a server.

Setting the envirronment

There's a file containing global configuration for GIT in your home directory. The file ~/.gitconfig will hold basic settings like your name, email address and global options. Here is an example of this configuration file:

[user]
	name = John Simth
	email = john.simth@epita.fr
[color]
	ui = true
[push]
	default = simple

SSH Key

One way to authenticate on a git server is to use ssh, most of the time using a public/private keys pair. If you haven't generate such a keys pair, here is a simple way to do it:

> ssh-keygen -t rsa -b 4096 -f ~/.ssh/my_git_key_file
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/login/.ssh/my_git_key_file.
Your public key has been saved in /home/login/.ssh/my_git_key_file.pub.
The key fingerprint is:
[ ... snip ... ]

Be sure that the directory ~/.ssh has been created before.

The public key that you will provides for the ACU intranet (providing authentification for git) will be my_git_key_file.pub.

You can add login information for the ACU's GIT services inside the file ~/.ssh/config:

Host git.acu.epita.fr
ForwardAgent yes
User git
IdentityFile ~/.ssh/my_git_key_file

You can now upload the key to the ACU's intranet [1] (deploy the menu under the login, and use the link related to ssh keys and follow the instructions.)

Cloning a repository

Now, you can clone projects' repository to start-up working, go to a working directory of your own, and then use git clone:

> git clone git@git.acu.epita.fr:2018/login-tp03.git

You have two projects already created: one for the practical session which is personal (login-tp03, where login is your login) and one for the OCR project of the semester (login-ocr2014 where login is the project leader login.)

Now we can do simple manipulations:

> cd login-tp03
> echo '* login' > AUTHORS
> git add AUTHORS
> git commit -m "Initial commit"
> git push -u origin master

OK, first 2 lines are obvious. In the third line, we add AUTHORS to the list of tracked files, then in the fourth line we commit the change with a simple message and finaly we push the change to the server (specifiying that we want the distant server called origin to be the default for push/pull.)

Basic Commands

Now you can try to edit in an other place the repository and synchronize your first version.

Using SDL

Installing the libs

First you need to add to your system the SDL library. With school hard-drive this can be done using apt-get:

> sudo apt-get install libsdl1.2-dev libsdl-image1.2-dev

Then check if you got all you need: pkg-config give you option for your compiler, among which you'll find the installation path of the lib. You should get similar input to mine:

> pkg-config --cflags sdl
-D_GNU_SOURCE=1 -D_REENTRANT -I/usr/include/SDL 
> ls /usr/include/SDL/SDL.h /usr/include/SDL/SDL_image.h
/usr/include/SDL/SDL.h  /usr/include/SDL/SDL_image.h

Simple Makefile

Here is a simple Makefile for the exercises using SDL:

## Simple SDL mini code
 
CC=clang
 
CPPFLAGS= `pkg-config --cflags sdl`
CFLAGS= -Wall -Wextra -Werror -std=c99 -O3
LDFLAGS=
LDLIBS= `pkg-config --libs sdl` -lSDL_image
 
SRC= pixel_operations.c main.c
OBJ= ${SRC:.c=.o}
 
all: main
 
main: ${OBJ}
 
clean:
	rm -f *~ *.o
	rm -f main
 
# END

Start-up code

First, we need to init SDL, open a window, load an image, display it and wait for a pressed key. This part of the code is not really interesting, I'll just give the code with some comments.

Waiting for a key:

void wait_for_keypressed(void) {
  SDL_Event             event;
  // Infinite loop, waiting for event
  for (;;) {
    // Take an event
    SDL_PollEvent( &event );
    // Switch on event type
    switch (event.type) {
    // Someone pressed a key -> leave the function
    case SDL_KEYDOWN: return;
    default: break;
    }
  // Loop until we got the expected event
  }
}

Initializing SDL:

void init_sdl(void) {
  // Init only the video part
  if( SDL_Init(SDL_INIT_VIDEO)==-1 ) {
    // If it fails, die with an error message
    errx(1,"Could not initialize SDL: %s.\n", SDL_GetError());
  }
  // We don't really need a function for that ...
}

Loading an image from a file:

SDL_Surface* load_image(char *path) {
  SDL_Surface          *img;
  // Load an image using SDL_image with format detection
  img = IMG_Load(path);
  if (!img)
    // If it fails, die with an error message
    errx(3, "can't load %s: %s", path, IMG_GetError());
  return img;
}

Now, we can write a function that take the surface corresponding to a loaded image and open a window with the same dimension, display the image on it and wait for a key:

SDL_Surface* display_image(SDL_Surface *img) {
  SDL_Surface          *screen;
  // Set the window to the same size as the image
  screen = SDL_SetVideoMode(img->w, img->h, 0, SDL_SWSURFACE|SDL_ANYFORMAT);
  if ( screen == NULL ) {
    // error management
    errx(1, "Couldn't set %dx%d video mode: %s\n",
         img->w, img->h, SDL_GetError());
  }
 
  /* Blit onto the screen surface */
  if(SDL_BlitSurface(img, NULL, screen, NULL) < 0)
    warnx("BlitSurface error: %s\n", SDL_GetError());
 
  // Update the screen
  SDL_UpdateRect(screen, 0, 0, img->w, img->h);
 
  // wait for a key
  wait_for_keypressed();
 
  // return the screen for further uses
  return screen;
}

Once you're finished with your image, you should free it using SDL_FreeSurface().

  • Put everything into a file main.c add the headers SDL/SDL.h and SDL/SDL_image.h and write the main function.

Pixel operations

These functions, derived from SDL documentation, are used to get a pixel value or to set it in a surface. They manage all image formats and return a unified type for a pixel that can be used with SDL_GetRGB and SDL_MapRGB to map the pixel to RGB component.

First, the header:

// pixel_operations.h
 
# ifndef PIXEL_OPERATIONS_H_
# define PIXEL_OPERATIONS_H_
 
# include <stdlib.h>
# include <SDL.h>
 
Uint32 getpixel(SDL_Surface *surface, unsigned x, unsigned y);
void putpixel(SDL_Surface *surface, unsigned x, unsigned y, Uint32 pixel);
 
# endif

Then the code:

// pixel_operations.c
 
// Simple get/put pixel for SDL
// Inspired by code from SDL documentation
// (http://www.libsdl.org/release/SDL-1.2.15/docs/html/guidevideo.html)
 
# include "pixel_operations.h"
 
static inline
Uint8* pixelref(SDL_Surface *surf, unsigned x, unsigned y) {
  int bpp = surf->format->BytesPerPixel;
  return (Uint8*)surf->pixels + y * surf->pitch + x * bpp;
}
 
Uint32 getpixel(SDL_Surface *surface, unsigned x, unsigned y) {
  Uint8 *p = pixelref(surface, x, y);
  switch(surface->format->BytesPerPixel) {
  case 1:
    return *p;
  case 2:
    return *(Uint16 *)p;
  case 3:
    if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
      return p[0] << 16 | p[1] << 8 | p[2];
    else
      return p[0] | p[1] << 8 | p[2] << 16;
  case 4:
    return *(Uint32 *)p;
  }
  return 0;
}
 
void putpixel(SDL_Surface *surface, unsigned x, unsigned y, Uint32 pixel) {
  Uint8 *p = pixelref(surface, x, y);
  switch(surface->format->BytesPerPixel) {
  case 1:
    *p = pixel;
    break;
  case 2:
    *(Uint16 *)p = pixel;
    break;
  case 3:
    if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
      p[0] = (pixel >> 16) & 0xff;
      p[1] = (pixel >> 8) & 0xff;
      p[2] = pixel & 0xff;
    } else {
      p[0] = pixel & 0xff;
      p[1] = (pixel >> 8) & 0xff;
      p[2] = (pixel >> 16) & 0xff;
    }
    break;
  case 4:
    *(Uint32 *)p = pixel;
    break;
  }
}

Converting an image to grey level

As an application will now iterate over the image in order to transform it into a grey image. Our conversion will use one of the classical conversion based on luminance definition. The basic version compute the average of the RGB components and then set all component to this value. While this is supposed to be correct, human perception of colors are slightly different: green appears brighter than red which is also brighter than blue. Thus, we'll compute a pondered average using the following coefficient: 0.3 for red, 0.59 for green and 0.11 for blue.

In order to convert the image you first need to be able to convert a given pixel:

  • from the Uint32 pixel value, you can obtain three Uint8 value using SDL_GetRGB(pixel, img->format, &r, &g, &b) where pixel is the pixel value, img is the surface corresponding to your image and r, g, b are three Uint8 variable that will contains the RGB components.
  • Using the previous coefficients compute the average value for the luminance (using a floating point variable to store the result)
  • Set r, g, b to the luminance
  • Get the new pixel value with SDL_MapRGB(img->format, r, g, b)

Once ready, you need to iterate over the pixel of the image: the surface provides img->w which is the width of the image and img->h which the height, just need a double for loop, the get and put pixel operations and this is it.

  • Extend the previous code (in main.c) so that after displaying the original image, it transforms the surface in grey level and blit it (take a look at the provided code) to the screen once again, then wait for a key and leave.