MiniLibX

hhkim·2021년 12월 22일
0

42cursus

목록 보기
4/20
post-thumbnail

opengl / mms

  • opengl은 .a 형식의 정적 라이브러리를,
    mms는 .dylib 형식의 동적 라이브러리를 생성
  • 사용할 수 있는 함수도 다름
    👉 정적 라이브러리에서 문제가 발생하는 경우가 있어서 동적 라이브러리 사용을 권장
    (make할 때 경고는 무시해도 된다고 함)
    (슬랙에서 최신 버전 찾아서 하니까 경고 안 뜸)

동적 라이브러리 경로 설정

  1. 직접 설정
    export DYLD_LIBRARY_PATH=[디렉토리]
    echo $DYLD_LIBRARY_PATH로 확인
  2. 명령어로 설정
    install_name_tool -change [파일명] [파일 경로] [적용할 파일(프로그램)]
    예) install_name_tool -change libmlx.dylib ./minilibx_mms_20210621/libmlx.dylib test
  3. 실행 파일과 같은 경로에 두는 방법

초기화

#include "mlx.h"

int	main(void)
{
	void	*m_ptr;
	void	*w_ptr;

	m_ptr = mlx_init();
	w_ptr = mlx_new_window(m_ptr, 1200, 800, "test");
	mlx_loop(m_ptr);
	return (0);
}

mlx_init

디스플레이와 연결 설정 (초기화)

void *mlx_init();
  • 성공 시 mlx 인스턴스의 위치를 가리키는 void * 리턴
    실패 시 NULL 리턴

mlx_new_window

새 창 생성

void *mlx_new_window(void *mlx_ptr, int size_x, int size_y, char *title);
  • 성공 시 해당 윈도우를 가리키는 void * 리턴
    실패 시 NULL 리턴

mlx_destroy_window

윈도우 포인터 할당 해제

int mlx_destroy_window(void *mlx_ptr, void *win_ptr);
  • 리턴값 없음

mlx_loop

창 렌더링
무한 루프를 돌면서 이벤트를 기다림

int mlx_loop(void *mlx_ptr);
  • 무한하게 루프를 돌기 때문에 리턴값이 없음
    (끝내려면 Ctrl + C)

컴파일

Makefile

CC = gcc
CFLAG = -Wall -Wextra -Werror
NAME = test

SRCS = main.c
OBJS = $(SRCS:.c=.o)

MLXDIR = ./minilibx_mms_20210621
MLXFLAG = -L$(MLXDIR) -lmlx -framework OpenGL -framework AppKit

.PHONY: all
all: $(NAME)

$(OBJS): $(SRCS)
	$(CC) $(CFLAG) -I$(MLXDIR) -c $(SRCS)

$(NAME): $(OBJS)
	@make -s -C $(MLXDIR)
	$(CC) $(CFLAG) $(OBJS) $(MLXFLAG) -o $(NAME)
	@install_name_tool -change libmlx.dylib ./minilibx_mms_20210621/libmlx.dylib test

...

픽셀 그리기

#include "mlx.h"

typedef struct	s_data
{
	void	*img;
	char	*addr;
	int	bits_per_pixel;
	int	line_length;
	int	endian;
}	t_data;

void	my_mlx_pixel_put(t_data *data, int x, int y, int color)
{
	char	*dst;

	dst = data->addr + (y * data->line_length + x * (data->bits_per_pixel / 8));
	*(unsigned int *)dst = color;
}

int	main(void)
{
	void	*mlx;
	void	*mlx_win;
	t_data	img;

	mlx = mlx_init();
	mlx_win = mlx_new_window(mlx, 1200, 800, "test");
	img.img = mlx_new_image(mlx, 1200, 800);
	img.addr = mlx_get_data_addr(img.img, &img.bits_per_pixel, &img.line_length,
			&img.endian);
	my_mlx_pixel_put(&img, 5, 5, 0x00FF0000);
	mlx_put_image_to_window(mlx, mlx_win, img.img, 0, 0);
	mlx_loop(mlx);
	return (0);
}
  • mlx_pixel_put은 아주 느리고 창이 모두 렌더링되기도 전에 픽셀을 창에 올림
    👉 픽셀을 버퍼에 담고 창이 렌더링된 후에 올리자!
  • 이미지의 정보는 char *에 담긴다.
  • 보통 픽셀은 int로 표현하기 때문에 4 바이트 (bits_per_pixel은 32)
    엔디안 방식에 따라 달라질 수 있다.
    예) 1 픽셀에 8 비트만 쓰는 경우 리틀 엔디안 사용...?

👉 bits_per_pixel이 32인 경우 1 픽셀은 char 4개로 표현
👉 원하는 픽셀(x, y)의 포인터는 현재 위치에 x * (bits_per_pixel / 8)y * line_length를 더한 만큼의 위치

색 표현

ARGB가 0 ~ 255 범위로 1 바이트씩 차지
예) 0x00FF0000은 빨강

mlx_new_image

새 이미지 생성

void *mlx_new_image(void *mlx_ptr, int width, int height);
  • 성공 시 해당 이미지 인스턴스를 가리키는 void * 리턴
    실패 시 NULL 리턴

mlx_get_data_addr

이미지의 정보 얻기

char *mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian);
  • img_ptr: 이미지 인스턴스 주소
    bits_per_pixel: 보통 픽셀은 int이므로 4 바이트, 즉 32 비트
    size_line: 이미지 가로 크기 * 1 픽셀 당 쓰이는 바이트 수 (?)
    endian: 엔디안 (0: 리틀 엔디안(인텔 기본), 1: 빅 엔디안)

    mms 라이브러리에서는 size_line에 패딩값이 추가되어 좀 더 큰 값이 들어간다.

  • 성공 시 첫 번째 픽셀의 첫 번째 바이트 주소 리턴
    실패 시 NULL 리턴

mlx_put_image_to_window

이미지 렌더링

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

Hooks

이벤트가 발생했을 때 OS의 행동을 인터셉트해서 실행하는 코드
(인터셉트한 함수, 이벤트, 메시지 등을 처리하는 코드)
👉 키보드 입력, 마우스 입력, 윈도우 부분 다시 그리기(?)에 사용

#include "mlx.h"
#include <stdio.h>

typedef struct	s_vars
{
	void	*mlx;
	void	*win;
}	t_vars;

int	key_hook(int keycode, t_vars *vars)
{
	(void)vars;
	printf("key hook: %d\n", keycode);
	return (0);
}

int	main(void)
{
	t_vars vars;

	vars.mlx = mlx_init();
	vars.win = mlx_new_window(vars.mlx, 600, 400, "test");
	mlx_key_hook(vars.win, key_hook, &vars);
	mlx_loop(vars.mlx);
	return (0);
}
  • 훅 함수의 인자
    win_ptr: 훅을 적용할 윈도우 주소
    funct_ptr: 키보드 입력 시 실행할 콜백 함수의 주소
    param: funct_ptr에 전달할 파라미터의 주소
  • 항상 0 리턴

mlx_key_hook

키보드 이벤트 설정

int mlx_key_hook(void *win_ptr, int (*funct_ptr)(), void *param);
  • 콜백 함수
    keycode: 입력된 키의 코드
int key_hook(int keycode, void *param);

mlx_mouse_hook

마우스 이벤트 설정

int mlx_mouse_hook(void *win_ptr, int (*funct_ptr)(), void *param);
  • 콜백 함수
    button: 입력된 마우스 버튼

    x, y: 현재 창에서의 좌표
mouse_hook(int button, int x, int y, void *param);

mlx_hook

모든 이벤트 설정

void mlx_hook(mlx_win_list_t *win_ptr, int x_event, int x_mask, int (*f)(), void *param)
  • MacOS에서 지원되는 이벤트
    (mask는 지원되지 않으므로 0으로 고정)
enum {
	ON_KEYDOWN = 2,
	ON_KEYUP = 3,
	ON_MOUSEDOWN = 4,
	ON_MOUSEUP = 5,
	ON_MOUSEMOVE = 6,
	ON_EXPOSE = 12,
	ON_DESTROY = 17
};
  • ON_DESTROY 콜백 함수
int hook_callback(void *param);

hook 함수들은 mlx_hook으로 대체하여 사용할 수 있다. 이 때 이벤트들은 X11의 X.h에 있는 이벤트와 마스크 값을 활용한다.

mlx_loop_hook

등록된 이벤트가 발생하지 않았을 때 반복적으로 실행할 함수 등록
👉 각 프레임을 업데이트하여 새로운 화면을 렌더링할 때 사용 (애니메이션)

int mlx_loop_hook(void *mlx_ptr, int (*funct_ptr)(), void *param);
  • 다른 hook과 다르게 mlx_ptr를 인자로 받는다.
  • 콜백 함수
int loop_hook(void *param);

이미지

파일을 읽어서 새로운 이미지 생성

  • 표준 xpm, png 라이브러리를 사용하지 않기 때문에 모든 종류의 xpm, png 이미지를 읽을 수는 없다.

    xpm
    X11에서 사용하는 이미지 확장자
    C 언어나 plain text로 되어 있음

...
int	main(void)
{
	void	*mlx;
	void	*mlx_win;
	t_data	img;
	int	img_width;
	int	img_height;

	mlx = mlx_init();
	mlx_win = mlx_new_window(mlx, 1200, 800, "test");
	img.img = mlx_png_file_to_image(mlx, "img.png", &img_width, &img_height);
	img.addr = mlx_get_data_addr(img.img, &img.bits_per_pixel, &img.line_length,
			&img.endian);
	mlx_put_image_to_window(mlx, mlx_win, img.img, 0, 0);
	mlx_loop(mlx);
	return (0);
}
  • mlx_ptr: mlx 포인터
    xpm_data, filename: 읽어올 xpm 데이터 또는 파일명 (경로는 실행파일 기준)
    width, height: 이미지의 크기를 저장할 변수의 주소
  • 성공 시 이미지 인스턴스의 주소 리턴
    실패 시 NULL 리턴

mlx_xpm_to_image

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

mlx_xpm_file_to_image

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

mlx_png_file_to_image

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

mlx_destroy_image

이미지 포인터 할당 해제

int mlx_destroy_image(void *mlx_ptr, void *img_ptr);

싱크

프레임 버퍼링을 직접 관리할 수도 있지만, 2020년 버전부터 mlx_sync가 지원됨

  • 동기화 작업이 없으면 GPU가 데이터를 언제 읽을지 알 수 없기 때문에 완성되지 않은 화면이 출력될 수도 있음
    👉 mlx_sync는 우리가 데이터를 다 쓰기까지 GPU가 기다리도록 함
mlx_sync(MLX_SYNC_IMAGE_WRITABLE, img);
mlx_put_image_to_window(mlx, win, img, 0, 0);
mlx_sync(MLX_SYNC_WIN_CMD_COMPLETED, win);

mlx_sync

효율적인 버퍼 관리를 위한 동기화 기능

#define MLX_SYNC_IMAGE_WRITABLE		1
#define MLX_SYNC_WIN_FLUSH_CMD		2
#define MLX_SYNC_WIN_CMD_COMPLETED	3

int	mlx_sync(int cmd, void *param);
  • cmd: 위에 정의된 매크로
    param: 각 매크로에 맞는 포인터
  • IMG_WRITABLE: 다음에 실행될 코드들(이미지 변경 사항들)을 버퍼에 저장, 두 번째 파라미터는 이미지 포인터
    WIN_FLUSH_CMD: 버퍼에 저장된 데이터를 내보내기 (GPU를 기다리지 않음), 두 번째 파라미터는 윈도우 포인터
    WIN_CMD_COMPLETED: 버퍼에 저장된 데이터를 내보내기 (GPU가 완료할 때까지 대기), 두 번째 파라미터는 윈도우 포인터

텍스트 쓰기

주어진 위치에 텍스트 쓰기

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

참고

https://harm-smits.github.io/42docs/libs/minilibx
https://42kchoi.tistory.com/229
https://bigpel66.oopy.io/library/c/etc/3
https://80000coding.oopy.io/adf8b9d2-475e-4bda-91a2-1d03df9e0d31

0개의 댓글