[so_long, cub3D] Minilibx(mlx) 톺아보기

Yejin Kim·2022년 6월 20일
0

42 cursus

목록 보기
13/20

해당 글은 minilibx의 man page를 기반으로 작성하였습니다


Minilibx 란?

42에서 Unix/Linux 기반의 X-Window/X11이나 MacOS 기반의 AppKit 관련 지식 없이도 쉽게 graphical software를 만들 수 있도록 제공하는 라이브러리
다음과 같은 기능들을 제공한다

  • 간단한 윈도우 생성
  • draw tool
  • image
  • event 관리
    - 키보드, 마우스 입력 등에 대한 management

코드에 include하여 실행하는 경우에는 mlx directory를 프로그램의 directory안에 넣어두고

-I $(mlx 경로) -L $(mlx 경로) -lmlx -framework OpenGl -framework AppKit

위와 같은 옵션을 추가해주어야 한다.


Let's see functions 👀

mlx_init

void *mlx_init()

software와 display를 연결해주는 함수이다.
내가 지금 만들고 있는 프로그램과 모니터를 연결시켜주는 것!

void *identifier를 return하는데, 이 identifier는 screen에 대한 식별자로 사용된다.

추상적으로 다가올 수 있는데, 결국 프로그램과 screen을 연결하고 그 screen에 대한 식별자를 생성하는 것 !

📍 Managing windows

mlx_new_window

void	*mlx_new_window(void *mlx_ptr, int size_x, int size_y, char *title)

screen에 새로운 window(창) 하나를 생성한다.
이 때 mlx_ptr에는 mlx_init으로 생성한 connection identifier를 넘겨줘야 하고,
size_x는 창의 width, size_y는 창의 height가 된다.
title은 해당 window의 title bar에 적힐 이름에 대한 인자이다.
위와 같이 코드를 작성하면 다음과 같은 창을 생성할 수 있다.

이 함수는 window identifier를 생성한다. 이 식별자는 생성된 창에 대한 식별자가 된다.
새 창을 생성하는데 실패하면 NULL을 반환한다.

Minilibx는 임의의 여러 window를 handle할 수 있음

mlx_clear_window

int	mlx_clear_window(void *mlx_ptr, void *win_ptr)

주어진 window를 검정 화면으로 clear한다.
현재는 아무것도 반환하지 않음.

mlx_destroy_window

int mlx_destroy_window(void *mlx_ptr, void *win_ptr)

주어진 window를 destroy한다.
즉, window에 대한 연결 해제를 하는 함수
현재는 아무것도 반환하지 않음.

win_ptr 자체가 아닌 mlx_ptr은 별도의 destroy 함수가 없다. 따로 메모리를 해제해주기에는 mlx_ptr에 할당되는 메모리 내부 구조가 굉장히 복잡하다.
해당 부분에 대한 메모리 해제는 거의 불가능하다고 판단하고, 나머지 부분에 대해서만 메모리를 전부 해제하고 mlx_ptr은 free()시키지 않고 포인터를 잃지 않은 채로 exit(EXIT_SUCCESS) 하는 것이 좋은 것 같다.
괜히 free(mlx_ptr)을 하면 해제되지 않은 내부 mlx_ptr 에 대해 누수가 발생할 수 있다.

📍 Drawing inside windows

mlx_pixel_put

int	mlx_pixel_put(void *mlx_ptr, void *win_ptr, int x, int y, int color)

window의 좌표 (x, y)에 color에 해당하는 색상의 pixel을 그린다.
(0, 0)는 window의 가장 왼쪽 윗 꼭짓점 좌표에 해당하고,
x축과 y축은 각각 오른쪽, 아래쪽을 가리키는 방향이다.

이 함수는 느리기 때문에 image 관리 함수들을 대신 사용하기를 권장한다고 한다

  • pixel 하나 하나를 직접 window에 출력하려고 하면 I/O 작업을 계속해야 하기 때문에 느리다.
  • 대신 window 크기만큼의 image data를 만들고 그 image data의 원하는 위치에 color 정보를 저장해둔 뒤 이 image를 한 번에 window에 출력하면 빠른 처리가 가능하다.

color management
color를 integer type으로 encode(암호화)하여 사용한다.
기본적으로는 색의 3원색에 해당하는 red, green, blue에 대한 정보를 각각 0~255 범위로 나타낸다.
그리고 hardware에 따라 최상위 1byte는 투명도를 나타내는데에 사용할 수 있다.

example)

  • red : 0xFF0000
  • green : 0x00FF00
  • blue : 0x0000FF
  • white : 0xFFFFFF

mlx_string_put

int	mlx_string_put(void *mlx_ptr, void *win_ptr, int x, int y, int color, char *string)

mlx_pixel_put과 달리 color 대신 char *string을 인자로 넘겨준다.
이 함수는 단순한 pixel이 아닌 string을 (x, y)에 출력한다.
크기는 따로 정하기는 힘들지만 위치와 색상, 메세지 내용을 정해줄 수 있다.

📍 Manipulating images

mlx_new_image

void *mlx_new_image(void *mlx_ptr, int width, int height)

메모리에 새로운 image를 생성하고, 그 image에 대한 식별자를 return 한다.

image 정보를 저장할 수 있는 공간이 할당되고, 그 곳에 대한 주소(식별자)를 return한다고 생각하면 쉽게 이해할 수 있다.

mlx_put_image_to_window

int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y)

이 함수를 통해 user는 screen window 안에 image를 그리거나 출력할 수 있다.
mlx_ptr, win_ptr, img_ptr은 각각 connection identifier, window identifier, image identifier이다.
image는 window의 (x, y)에 출력된다.

mlx_get_data_addr

char *mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian)

생성된 image에 대한 정보를 알려주는 함수이다.
img_ptr은 사용할 image에 대한 식별자이다.
나머지 3개의 인자는 integer 변수의 주소를 넘겨줘서 정보를 받을 수 있도록 한다.

  • bits_per_pixel은 pixel의 색을 표현하기 위해 필요한 bit 수에 대한 정보를 받기 위한 변수
  • size_line은 메모리 상에서 이미지의 한 줄이 몇 byte로 저장되어 있는지에 대한 정보를 받기 위한 변수
    • 이 정보는 image에서 어떤 line에서 어떤 line으로
  • endian은 pixel color가 little endian 방식으로 저장되어 있는지, big endian 방식으로 저장되어 있는지에 대한 정보를 저장하기 위한 변수
    big endian인 경우 1, little endian인 경우 0이 저장된다.

    endian 이란?
    컴퓨터가 데이터를 byte 단위로 저장하는데, 컴퓨터가 byte들을 메모리에 어떤 순서로 저장하는지 여부가 CPU 에 따라 달라질 수 있다.
    이 byte order는 크게 big endianlittle endian으로 나뉜다.

    • big endian : 가장 큰 데이터에서 작은 데이터 순으로 저장
      • ex) 0x12345678을 저장할 때 12, 34, 56, 78 순으로 저장한다.
    • little endian : 가장 작은 데이터에서 가장 큰 데이터 순으로 저장
      • ex) 0x12345678을 저장할 때 78, 56, 34, 12 순으로 저장한다.
        big endian은 인간이 쓰는 형태와 동일하기 때문에 해당 방식을 사용하는 소프트웨어를 디버그할 때 편하다. 반면 little endian의 경우 메모리에 저장된 값들 중 하위 byte만을 추출해서 사용하는 연산이 많은 경우, 별도의 계산없이 앞에서부터 원하는 하위 n byte만 읽으면 된다는 장점이 있다.


data 이미지를 저장해둬야 하는 이유는 컴퓨터마다 이미지를 저장하는 방식이 다를 수 있기 때문으로 이해했다.
이미지 데이터가 실제로 각 픽셀에 대한 컬러 정보를 나열한 형태(1차원)로 저장이 되어 있는데, 결국에 시각적으로 볼 때는 2차원으로 읽어내야하므로 이에 대한 정보가 필요하다.

  • 1차원 정보에서 2차원으로 변환할 때 한 줄의 길이에 대한 정보가 필요(size_line)
  • 하나의 픽셀의 color에 대한 정보를 몇 바이트에 담아놓았는 지에 대한 정보가 필요하다 (bits_per_pixel).

mlx_get_color_value

unsigned int	mlx_get_color_value(void *mlx_ptr, int color)

디스플레이에 따라서 pixel의 color를 저장하는 bit의 수가 달라질 수 있다.
user는 보통 한 component(ex. pixel)에 대해 color를 RGB mode로 표현한다.
이 color 저장 값은 이미지가 요구하는 bits_per_pixel에 맞춰 해당 디스플레이가 이해할 수 있도록 변환해야 하는데, 이 때 mlx_get_color_value를 사용한다.
standard RGB color를 인자로 받고 unsigned int 값을 return 한다.
이 return 값의 하위 bits_per_pixel 개의 비트들은 image에 저장될 수 있다.
변환이 필요없는 케이스들에 대해서는 사용을 하지 않아도 된다.

mlx_xpm_to_image

void *mlx_xpm_to_image(void *mlx_ptr, char **xpm_data, int *width, int *height)

xpm_data를 인자로 받아 이미지 데이터를 생성하고 그 이미지를 해당 데이터를 바탕으로 채운다.
성공 시, 해당 이미지에 대한 식별자를 반환하고
실패 시, NULL을 반환

mlx_xpm_file_to_image

void *mlx_xpm_file_to_image(void *mlx_ptr, char *filename, int *width, int *height)

xpm file의 파일명을 인자로 받아 이미지 데이터를 생성하고 그 이미지를 해당 데이터를 바탕으로 채운다.
성공 시, 해당 이미지에 대한 식별자를 반환하고
실패 시, NULL을 반환

mlx_png_file_to_image

void *mlx_png_file_to_image(void *mlx_ptr, char *filename, int *width, int *height)

png file의 파일명을 인자로 받아 이미지 데이터를 생성하고 그 이미지를 해당 데이터를 바탕으로 채운다.
성공 시, 해당 이미지에 대한 식별자를 반환하고
실패 시, NULL을 반환

png file을 이미지 파일로 변환하는 경우 메모리 누수가 발생한다는 issue가 있어 대부분 xpm 파일을 사용하는 것을 추천하여 png file을 xpm file로 변환하여 프로그램에 사용하였다.

mlx_destroy_image(void mlx_ptr, void img_ptr)

int	mlx_destroy_image(void *mlx_ptr, void *img_ptr)

📍 Handle keyboard or mouse events

그래픽 시스템은 screen에 연결된 키보드나 마우스로부터 정보를 받고 화면에 픽셀을 출력한다.

mlx_loop

int	mlx_loop (void *mlx_ptr)

이벤트를 받기 위해서 반드시 사용해야 하는 함수이다.
return을 하지 않으며 event를 기다리는 무한 반복 loop이 실행된다.
event가 들어오면 해당 event에 관련된 user-define 함수를 call한다.
mlx_ptr을 인자로 필요로 한다.

mlx_key_hook, mlx_mouse_hook, mlx_expose_hook

int	mlx_key_hook (void *win_ptr, int (*funct_ptr)(), void *param)
int	mlx_mouse_hook (void *win_ptr, int (*funct_ptr)(), void *param)
int	mlx_expose_hook (void *win_ptr, int (*funct_ptr)(), void *param)

세 함수는 거의 똑같은 방식으로 작동된다.
funct_ptr은 event가 발생했을 때 실행할 함수에 대한 포인터이다.
param은 함수가 호출될 때마다 함수에 넘겨주는 인자이다.

event가 감지되면 mlx는 해당 함수를 다음과 같은 인자 형식으로 호출한다.

expose_hook(void param)
key_hook(int keycode, void
param)
mouse_hook(int button, int x, int y, void param)
loop_hook(void
param)
명시적으로 넘겨줄 인자는 param에 넣어주면 되고, 나머지는 mlx가 알아서 해당 함수를 호출할 때 넘겨준다.

key나 mouse 이벤트가 발생했을 때 추가적으로 keycode에 대한 정보를 전달한다. keycode는 어떤 key가 눌리는 event가 발생했는지를 알려준다.
x, y 는 mouse가 window에 클릭한 위치에 대한 좌표를 의미하고, button은 mouse의 어떤 버튼이 눌렸는지 정보를 알려준다.


mlx_loop_hook

int	mlx_loop_hook (void *win_ptr, int (*funct_ptr)(), void *param)

위의 세 함수들과 거의 비슷한데, 이 함수의 경우에는 event가 발생하지 않았을 때에도 계속해서 호출이 된다.

cub3D의 경우에는 자연스러운 움직임 구현이 강조되는 프로젝트이기 때문에 이벤트가 발생하지 않았더라도 계속해서 렌더링하는 구현을 하는 것도 좋은 방법인 것 같다.


🦋 so_long repo address
https://github.com/kyj93790/42-cursus/tree/master/so_long

🦋 raycaster repo address
https://github.com/kyj93790/raycaster

🦋 cub3D repo address
https://github.com/sprint42/cub3D


참고 사이트
https://eastmanreference.com/complete-list-of-applescript-key-codes

profile
The World Is My Oyster 🌏

0개의 댓글