20171002:Practical:C:SDL:Arrays
Sommaire
Introduction
This session will introduce some array manipulations and simple image loading and manipulation using the SDL library.
For this session, you must (after setting-up your GIT) create a directory in your GIT repository for tutorials named 20171002_tuto01 with the following architectures:
- 20171002_tuto01/
- Arrays/
- this directory will contains code for the first series of exercises (arrays)
- SDL/
- this directory will contains code for the last series of exercises (SDL introduction)
- Arrays/
Submission
- S3: You must have commit and push your code before next monday (the 9th of october) at 2pm.
- S3#: It is due on Friday, February 23, 2018 at 2pm (this session spans across two weeks).
GIT set-up
In case you still got issues with your GIT set-up check (again) Practical:GIT.
You need GIT in order to submit the code of your tutorial sessions and your project.
Arrays
The following serie of exercises introduces basic array manipulation.
Provided code
We provide some helper code (for testing) in the form of a header (tests_tools.h), a Makefile and for each exercise functions and main for testing.
This part describes the code common to all exercises, specific files will be provided in exercises parts.
/* tests_tools.h */ /* Tools for testing array functions */ # ifndef EPITA_IP_ARRAY_TESTS_TOOLS_H_ # define EPITA_IP_ARRAY_TESTS_TOOLS_H_ /* * Needed in order to use some POSIX functions * see feature_test_macros(7) */ # define _XOPEN_SOURCE 500 # include <stdio.h> # include <stdlib.h> /* * helper functions (array fill, display ... ) * all functions are statically inlined, no links dependencies */ /* * array_random_fill(array, len, maxval): fill array with random values * maxval is the maximum random value */ static inline void array_random_fill(int array[], size_t len, int maxval) { for (size_t i = 0; i < len; ++i) array[i] = random() % maxval; } /* * array_sorted_fill(array, len, step): fill array with sorted value * step is the difference between values */ static inline void array_sorted_fill(int array[], size_t len, int step) { int cur = 0; for (size_t i = 0; i < len; ++i, cur += step) array[i] = cur; } /* * array_reverse_sorted_fill(array, len, step): fill array with values in * reverse order * step is the difference between values */ static inline void array_reverse_sorted_fill(int array[], size_t len, int step) { int cur = (len - 1) * step; for (size_t i = 0; i < len; ++i, cur -= step) array[i] = cur; } /* * int_len(int x): number of decimal digit */ static inline int int_len(int x) { int len = 0; for (; x > 0; x /= 10) len += 1; return len; } /* * array_print(array, len): print the array */ static inline void array_print(int array[], size_t len, int maxval) { int line = 0; int intlen = int_len(maxval); for (size_t i = 0; i < len; ++i) { if (line > 72) { printf("|\n"); line = 0; } line += printf("| %*d ", intlen, array[i]); } printf("|\n"); } #define check(C__) ((C__) ? "\x1b[44mOK\x1b[0m" : "\x1b[41mKO\x1b[0m") # endif /* EPITA_IP_ARRAY_TESTS_TOOLS_H_ */
# Simple Makefile # Compilers vars CC=gcc CPPFLAGS= -MMD CFLAGS= -Wall -Wextra -std=c99 -O2 LDFLAGS= LDLIBS= SRC= array_min_max.c array_searching.c array_sorting.c DEP= ${SRC:.c=.d} PRG= ${SRC:.c=} all: ${PRG} -include ${DEP} clean: rm -f *.o rm -f ${DEP} rm -f ${PRG} # END Makefile
Minimal and maximal values in array
Your goal in this part is to implement the following functions:
/* * array_min(array, len): returns min value of array */ int array_min(int array[], size_t len); /* * array_max(array, len): returns max value of array */ int array_max(int array[], size_t len); /* * array_min_index(array, len): returns the index of the min value of array */ size_t array_min_index(int array[], size_t len); /* * array_max_index(array, len): returns the index of the max value of array */ size_t array_max_index(int array[], size_t len);
- array_min(array, len): find the minimal value of array (of size len) and returns it.
- array_max(array, len): find the maximal value of array (of size len) and returns it.
- array_min_index(array, len): find the minimal value of array (of size len) and returns its index.
- array_max_index(array, len): find the maximal value of array (of size len) and returns its index.
You'll put your code in the file array_min_max.c (look for FIX ME comments) with some provided tests:
/* array_min_max.c: min and max searching */ # define _XOPEN_SOURCE 500 # include <assert.h> # include <err.h> # include <stdio.h> # include <stdlib.h> # include "tests_tools.h" # define MAXVAL 100 # define STEP 10 /* * In all following functions parameters array and len are defined as: * array: an integer array * len: the length of the array, must be greater than 0 */ /* * min and max value */ /* * array_min(array, len): returns min value of array */ int array_min(int array[], size_t len) { // FIX ME } /* * array_max(array, len): returns max value of array */ int array_max(int array[], size_t len) { // FIX ME } /* * min and max index */ /* * array_min_index(array, len): returns the index of the min value of array */ size_t array_min_index(int array[], size_t len) { // FIX ME } /* * array_max_index(array, len): returns the index of the max value of array */ size_t array_max_index(int array[], size_t len) { // FIX ME } /* * Tests */ /* * Test functions */ /* * min value tests */ void test_min(int array[], size_t len) { int min; printf("*** Test min value search ***\n"); printf("**** Sorted array ****\n"); array_sorted_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); min = array_min(array, len); printf(" array_min: %d\t(should be %d)\n", min, array[0]); printf("**** Reverse sorted array ****\n"); array_reverse_sorted_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); min = array_min(array, len); printf(" array_min: %d\t(should be %d)\n", min, array[len - 1]); printf("**** Random array ****\n"); array_random_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); min = array_min(array, len); printf(" array_min: %d\n", min); } /* * max value tests */ void test_max(int array[], size_t len) { int max; printf("*** Test max value search ***\n"); printf("**** Sorted array ****\n"); array_sorted_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); max = array_max(array, len); printf(" array_max: %d\t(should be %d)\n", max, array[len - 1]); printf("**** Reverse sorted array ****\n"); array_reverse_sorted_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); max = array_max(array, len); printf(" array_max: %d\t(should be %d)\n", max, array[0]); printf("**** Random array ****\n"); array_random_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); max = array_max(array, len); printf(" array_max: %d\n", max); } /* * min index test */ void test_min_index(int array[], size_t len) { size_t min; printf("*** Test min value index search ***\n"); printf("**** Sorted array ****\n"); array_sorted_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); min = array_min_index(array, len); printf(" array_min_index: array[%zu] = %d\n", min, array[min]); printf("**** Reverse sorted array ****\n"); array_reverse_sorted_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); min = array_min_index(array, len); printf(" array_min_index: array[%zu] = %d\n", min, array[min]); printf("**** Random array ****\n"); array_random_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); min = array_min_index(array, len); printf(" array_min_index: array[%zu] = %d\n", min, array[min]); } /* * max index test */ void test_max_index(int array[], size_t len) { size_t max; printf("*** Test max value index search ***\n"); printf("**** Sorted array ****\n"); array_sorted_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); max = array_max_index(array, len); printf(" array_max_index: array[%zu] = %d\n", max, array[max]); printf("**** Reverse sorted array ****\n"); array_reverse_sorted_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); max = array_max_index(array, len); printf(" array_max_index: array[%zu] = %d\n", max, array[max]); printf("**** Random array ****\n"); array_random_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); max = array_max_index(array, len); printf(" array_max_index: array[%zu] = %d\n", max, array[max]); } /* * main */ int main(int argc, char *argv[]) { size_t len; if (argc < 2) errx(1, "must provide array length"); len = strtoul(argv[1], NULL, 10); int *array = calloc(len, sizeof (int)); test_min(array, len); printf("\n"); test_max(array, len); printf("\n"); test_min_index(array, len); printf("\n"); test_max_index(array, len); printf("\n"); free(array); return 0; }
You can compile and test your code with:
shell> make array_min_max gcc -fsanitize=address -Wall -Wextra -std=c99 -O2 -MMD array_min_max.c -o array_min_max shell> ./array_min_max 10 ...
Searching in an array
For this exercise, you need to implement two searching functions: a basic linear search and a binary search in a sorted array.
Here are the prototype for this functions:
/* * array_find(array, len, x): returns the position of x in array or len if not * present */ size_t array_find(int array[], size_t len, int x); /* * array_bin_search(array, len, x): search in a sorted array using binary search * returns the position of x, or the expected position of x if not present */ size_t array_bin_search(int array[], size_t len, int x);
- array_find(array, len, x) returns the position of x in array or len if not present
- array_bin_search(array, len, x) search in a sorted array using binary search returns the position of x, or the expected position of x if not present
The binary search algorithm (you should know it) works as follow:
bin_search(array, left, right, x): if left == right: return right mid = left + (right - left) / 2 if array[mid] == x: return mid if x < array[mid]: return bin_search(array, left, mid, x) else: return bin_search(array, mid + 1, right, x)
Of course, you can transform it into a loop … Note also that the binary search should return, if x is not present, the insertion position of x: that is where we should insert it if we want to preserve order in our array.
Like the previous exercise, here is the main file with test code (array_searching.c):
/* searching in array */ # define _XOPEN_SOURCE 500 # include <assert.h> # include <err.h> # include <stdio.h> # include <stdlib.h> # include <time.h> # include "tests_tools.h" # define MAXVAL 100 # define STEP 10 /* * In all following functions parameters array and len are defined as: * array: an integer array * len: the length of the array, must be greater than 0 */ /* * searching for value */ /* * array_find(array, len, x): returns the position of x in array or len if not * present */ size_t array_find(int array[], size_t len, int x) { // FIX ME } /* * array_bin_search(array, len, x): search in a sorted array using binary search * returns the position of x, or the expected position of x if not present */ size_t array_bin_search(int array[], size_t len, int x) { // FIX ME } /* * Tests */ int array_random_sorted_fill(int array[], size_t len, int maxstep) { int cur = 0; for (size_t i = 0; i < len; ++i) { int step = 1 + random() % maxstep; cur += step; array[i] = cur; } return cur; } typedef size_t (*search_fun)(int *, size_t, int); static inline void search_help(search_fun fun, int array[], size_t len, int x) { printf(" searching %d: ", x); size_t i = fun(array, len, x); if (i < len) printf("array[%zu] = %d\n", i, array[i]); else printf("not found (%zu)\n", i); } static void test_search(int array[], size_t len) { int x; printf("*** Test value search ***\n"); printf("**** Sorted array ****\n"); array_sorted_fill(array, len, STEP); array_print(array, len, (len - 1) * STEP); x = array[0]; search_help(array_find, array, len, x); x = array[len - 1]; search_help(array_find, array, len, x); x = array[random() % len]; search_help(array_find, array, len, x); x = len * STEP; search_help(array_find, array, len, x); printf("**** Reverse sorted array ****\n"); array_reverse_sorted_fill(array, len, STEP); array_print(array, len, (len - 1) * STEP); x = array[0]; search_help(array_find, array, len, x); x = array[len - 1]; search_help(array_find, array, len, x); x = array[random() % len]; search_help(array_find, array, len, x); x = len * STEP; search_help(array_find, array, len, x); printf("**** Random array ****\n"); array_random_fill(array, len, MAXVAL); array_print(array, len, MAXVAL); x = array[0]; search_help(array_find, array, len, x); x = array[len - 1]; search_help(array_find, array, len, x); x = array[random() % len]; search_help(array_find, array, len, x); x = random() % (MAXVAL * 2); search_help(array_find, array, len, x); } static void test_bin_search(int array[], size_t len) { int x; printf("*** Test value binary search ***\n"); printf(" founded index may not contain searched value\n"); printf("**** Basic sorted array ****\n"); array_sorted_fill(array, len, STEP); array_print(array, len, (len - 1) * STEP); x = array[0]; search_help(array_bin_search, array, len, x); x = array[len - 1]; search_help(array_bin_search, array, len, x); x = array[random() % len]; search_help(array_bin_search, array, len, x); x = len * STEP; search_help(array_bin_search, array, len, x); printf("**** Basic sorted array ****\n"); int maxval = array_random_sorted_fill(array, len, STEP); array_print(array, len, maxval); x = array[0]; search_help(array_bin_search, array, len, x); x = array[len - 1]; search_help(array_bin_search, array, len, x); x = array[random() % len]; search_help(array_bin_search, array, len, x); x = random() % (maxval * 2); search_help(array_bin_search, array, len, x); } /* * main */ int main(int argc, char *argv[]) { srandom(time(NULL)); size_t len; if (argc < 2) errx(1, "must provide array length"); len = strtoul(argv[1], NULL, 10); int *array = calloc(len, sizeof (int)); test_search(array, len); printf("\n"); test_bin_search(array, len); printf("\n"); free(array); return 0; }
Selection sort
We, now, want to implement a selection sort. Select sort works as follow:
select_sort(array, len): for i in range(len): # find min index in array between i and len min = array_min(array[i:len]) swap(array[i], array[min])
As you can see, we need a function that find the index of the minimal value in an array (we've already done it !) and a function for swapping array cells. We also want a function that check if an array is sorted (for tests). For the index of the minimal value, you are free to reuse your code (you can call your function shifted by i position with: array_min_index(array + i, len - i) but beware that you need to shift the resulting index by i again) or write a specific version or simply include the code in the sort function.
Here are the prototypes for the expected functions:
/* * array_is_sorted(array, len): returns true (not 0) if array is sorted in * increasing order */ int array_is_sorted(int array[], size_t len); /* * array_swap(array, i, j): swap cell value at position i and j */ void array_swap(int array[], size_t i, size_t j); /* Selection sort */ /* * array_select_sort(array, len): sort array using select sort */ void array_select_sort(int array[], size_t len);
And like the other exercises, the code for testing:
/* array_sorting.c : sorting arrays */ # define _XOPEN_SOURCE 500 # include <assert.h> # include <err.h> # include <stdio.h> # include <stdlib.h> # include <time.h> # include "tests_tools.h" # define MAXVAL 100 # define STEP 10 /* * In all following functions parameters array and len are defined as: * array: an integer array * len: the length of the array, must be greater than 0 */ /* tools for sorting */ /* * array_is_sorted(array, len): returns true if array is sorted in increasing * order */ int array_is_sorted(int array[], size_t len) { // FIX ME } /* * array_swap(array, i, j): swap cell value at position i and j */ void array_swap(int array[], size_t i, size_t j) { // FIX ME } /* Selection sort */ /* * array_select_sort(array, len): sort array using select sort */ void array_select_sort(int array[], size_t len) { // FIX ME } /* * Tests */ static inline double time_gdiff(struct timespec t0, struct timespec t1) { double s = t1.tv_sec - t0.tv_sec; s += (t1.tv_nsec - t0.tv_nsec) * 1e-9; return s; } static inline void sort_help(int array[], size_t len, int maxval) { struct timespec t0, t1; printf("Array before sort:\n"); array_print(array, len, maxval); printf(" ... sorting ...\n"); clock_gettime(CLOCK_MONOTONIC, &t0); array_select_sort(array, len); clock_gettime(CLOCK_MONOTONIC, &t1); printf("Array after sort:\n"); array_print(array, len, maxval); printf(" time for sorting: %gs\n", time_gdiff(t0, t1)); printf(" sort check: %s\n", check(array_is_sorted(array, len))); } void test_sorting(int array[], size_t len) { printf("*** Sorted array ***\n"); array_sorted_fill(array, len, STEP); sort_help(array, len, STEP * (len - 1)); printf("\n*** Reverse sorted array ***\n"); array_reverse_sorted_fill(array, len, STEP); sort_help(array, len, STEP * (len - 1)); printf("\n*** Random array ***\n"); array_random_fill(array, len, MAXVAL); sort_help(array, len, MAXVAL); } /* * main */ int main(int argc, char *argv[]) { srandom(time(NULL)); size_t len; if (argc < 2) errx(1, "must provide array length"); len = strtoul(argv[1], NULL, 10); int *array = calloc(len, sizeof (int)); test_sorting(array, len); printf("\n"); free(array); return 0; }
Using SDL
Checking for the libs
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=gcc CPPFLAGS= `pkg-config --cflags sdl` -MMD CFLAGS= -Wall -Wextra -Werror -std=c99 -O3 LDFLAGS= LDLIBS= `pkg-config --libs sdl` -lSDL_image OBJ= pixel_operations.o main.o DEP= ${OBJ:.o=.d} all: main main: ${OBJ} clean: ${RM} ${OBJ} ${DEP} *~ ${RM} main -include ${DEP} # 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.