color
This commit is contained in:
parent
8aecd02ea7
commit
6788c6f07b
|
@ -1,6 +1,8 @@
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(cgbwebcam)
|
project(cgbwebcam)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
set(SDL_STATIC ON)
|
set(SDL_STATIC ON)
|
||||||
set(SDL_SHARED OFF)
|
set(SDL_SHARED OFF)
|
||||||
|
|
||||||
|
|
223
main.c
223
main.c
|
@ -1,3 +1,4 @@
|
||||||
|
#include "SDL_blendmode.h"
|
||||||
#include "SDL_error.h"
|
#include "SDL_error.h"
|
||||||
#include "SDL_stdinc.h"
|
#include "SDL_stdinc.h"
|
||||||
#include "SDL_surface.h"
|
#include "SDL_surface.h"
|
||||||
|
@ -17,19 +18,21 @@
|
||||||
#include "mgba/feature/commandline.h"
|
#include "mgba/feature/commandline.h"
|
||||||
#include "mgba-util/image.h"
|
#include "mgba-util/image.h"
|
||||||
|
|
||||||
|
#define NUM_CHANNELS 3
|
||||||
|
|
||||||
static SDL_Window* window = NULL;
|
static SDL_Window* window = NULL;
|
||||||
static SDL_Renderer* renderer = NULL;
|
static SDL_Renderer* renderer = NULL;
|
||||||
static SDL_Camera* camera = NULL;
|
static SDL_Camera* camera = NULL;
|
||||||
static SDL_CameraSpec spec;
|
static SDL_CameraSpec spec;
|
||||||
static SDL_Texture* texture = NULL;
|
static SDL_Texture* textures[NUM_CHANNELS] = { NULL, NULL, NULL };
|
||||||
static SDL_bool texture_updated = SDL_FALSE;
|
static SDL_bool texture_updated = SDL_FALSE;
|
||||||
static SDL_Surface* frame_current = NULL;
|
|
||||||
static SDL_CameraDeviceID front_camera = 0;
|
static SDL_CameraDeviceID front_camera = 0;
|
||||||
static SDL_CameraDeviceID back_camera = 0;
|
static SDL_CameraDeviceID back_camera = 0;
|
||||||
static SDL_Surface* scaled = NULL;
|
static SDL_Surface* scaled = NULL;
|
||||||
static SDL_Surface* screen = NULL;
|
static SDL_Surface* filtered[NUM_CHANNELS] = { NULL, NULL, NULL };
|
||||||
|
static SDL_Surface* screens[NUM_CHANNELS] = { NULL, NULL, NULL };
|
||||||
|
|
||||||
static struct mCore* core = NULL;
|
static struct mCore* cores[NUM_CHANNELS] = { NULL, NULL, NULL };
|
||||||
|
|
||||||
enum mColorFormat pixfmt_sdl_to_mgba(uint32_t sdl_fmt) {
|
enum mColorFormat pixfmt_sdl_to_mgba(uint32_t sdl_fmt) {
|
||||||
switch (sdl_fmt) {
|
switch (sdl_fmt) {
|
||||||
|
@ -54,47 +57,90 @@ enum mColorFormat pixfmt_sdl_to_mgba(uint32_t sdl_fmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void myStartRequestImage(struct mImageSource* self, unsigned w, unsigned h, int colorFormats) {
|
void myStartRequestImageRed(struct mImageSource* self, unsigned w, unsigned h, int colorFormats) {
|
||||||
SDL_DestroySurface(scaled);
|
SDL_DestroySurface(scaled);
|
||||||
|
SDL_DestroySurface(filtered[0]);
|
||||||
scaled = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
scaled = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
||||||
|
filtered[0] = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
||||||
|
|
||||||
SDL_Log("scaled: %d x %d", w, h);
|
SDL_Log("red: %d x %d", w, h);
|
||||||
|
|
||||||
// SDL_DestroyTexture(texture);
|
// SDL_DestroyTexture(texture);
|
||||||
// texture = SDL_CreateTextureFromSurface(renderer, scaled);
|
// texture = SDL_CreateTextureFromSurface(renderer, scaled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void myStopRequestImage(struct mImageSource* self) {
|
void myStartRequestImageGreen(struct mImageSource* self, unsigned w, unsigned h, int colorFormats) {
|
||||||
|
SDL_DestroySurface(filtered[1]);
|
||||||
|
filtered[1] = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
||||||
|
SDL_Log("green: %d x %d", w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void myStartRequestImageBlue(struct mImageSource* self, unsigned w, unsigned h, int colorFormats) {
|
||||||
|
SDL_DestroySurface(filtered[2]);
|
||||||
|
filtered[2] = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
||||||
|
SDL_Log("blue: %d x %d", w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void myStopRequestImageRed(struct mImageSource* self) {
|
||||||
SDL_DestroySurface(scaled);
|
SDL_DestroySurface(scaled);
|
||||||
|
SDL_DestroySurface(filtered[0]);
|
||||||
scaled = NULL;
|
scaled = NULL;
|
||||||
|
filtered[0] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void myRequestImage(struct mImageSource* self, const void** buf, size_t* stride, enum mColorFormat* fmt) {
|
void myStopRequestImageGreen(struct mImageSource* self) {
|
||||||
if (scaled != NULL && frame_current != NULL) {
|
SDL_DestroySurface(filtered[1]);
|
||||||
struct SDL_Rect srcrect = frame_current->clip_rect;
|
filtered[1] = NULL;
|
||||||
int w = srcrect.h * scaled->clip_rect.w / scaled->clip_rect.h;
|
|
||||||
if (w >= srcrect.w) {
|
|
||||||
srcrect.x = (srcrect.w - w) / 2;
|
|
||||||
srcrect.w = w;
|
|
||||||
} else {
|
|
||||||
int h = srcrect.w * scaled->clip_rect.h / scaled->clip_rect.w;
|
|
||||||
srcrect.y = (srcrect.h - h) / 2;
|
|
||||||
srcrect.h = h;
|
|
||||||
}
|
}
|
||||||
int res = SDL_BlitSurfaceScaled(frame_current, &srcrect, scaled, &scaled->clip_rect, SDL_SCALEMODE_NEAREST);
|
|
||||||
if (res != 0) {
|
void myStopRequestImageBlue(struct mImageSource* self) {
|
||||||
SDL_Log("failed to scale: %s", SDL_GetError());
|
SDL_DestroySurface(filtered[2]);
|
||||||
|
filtered[2] = NULL;
|
||||||
}
|
}
|
||||||
*fmt = pixfmt_sdl_to_mgba(scaled->format->format);
|
|
||||||
*stride = scaled->pitch / (BYTES_PER_PIXEL / scaled->format->bytes_per_pixel);
|
void myRequestImageRed(struct mImageSource* self, const void** buf, size_t* stride, enum mColorFormat* fmt) {
|
||||||
*buf = scaled->pixels;
|
SDL_Surface* source = filtered[0];
|
||||||
|
if (source != NULL) {
|
||||||
|
*fmt = pixfmt_sdl_to_mgba(source->format->format);
|
||||||
|
*stride = source->pitch / (BYTES_PER_PIXEL / source->format->bytes_per_pixel);
|
||||||
|
*buf = source->pixels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct mImageSource gb_img_src = {
|
void myRequestImageGreen(struct mImageSource* self, const void** buf, size_t* stride, enum mColorFormat* fmt) {
|
||||||
.requestImage = myRequestImage,
|
SDL_Surface* source = filtered[1];
|
||||||
.startRequestImage = myStartRequestImage,
|
if (source != NULL) {
|
||||||
.stopRequestImage = myStopRequestImage,
|
*fmt = pixfmt_sdl_to_mgba(source->format->format);
|
||||||
|
*stride = source->pitch / (BYTES_PER_PIXEL / source->format->bytes_per_pixel);
|
||||||
|
*buf = source->pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void myRequestImageBlue(struct mImageSource* self, const void** buf, size_t* stride, enum mColorFormat* fmt) {
|
||||||
|
SDL_Surface* source = filtered[2];
|
||||||
|
if (source != NULL) {
|
||||||
|
*fmt = pixfmt_sdl_to_mgba(source->format->format);
|
||||||
|
*stride = source->pitch / (BYTES_PER_PIXEL / source->format->bytes_per_pixel);
|
||||||
|
*buf = source->pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct mImageSource gb_img_src[3] = {
|
||||||
|
{
|
||||||
|
.requestImage = myRequestImageRed,
|
||||||
|
.startRequestImage = myStartRequestImageRed,
|
||||||
|
.stopRequestImage = myStopRequestImageRed,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.requestImage = myRequestImageGreen,
|
||||||
|
.startRequestImage = myStartRequestImageGreen,
|
||||||
|
.stopRequestImage = myStopRequestImageGreen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.requestImage = myRequestImageBlue,
|
||||||
|
.startRequestImage = myStartRequestImageBlue,
|
||||||
|
.stopRequestImage = myStopRequestImageBlue,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
int SDL_AppInit(int argc, char *argv[]) {
|
int SDL_AppInit(int argc, char *argv[]) {
|
||||||
|
@ -112,7 +158,8 @@ int SDL_AppInit(int argc, char *argv[]) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
core = mCoreFind(args.fname);
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||||
|
struct mCore* core = mCoreFind(args.fname);
|
||||||
if (!core || !core->init(core)) {
|
if (!core || !core->init(core)) {
|
||||||
SDL_Log("Couldn't initialize mgba core!");
|
SDL_Log("Couldn't initialize mgba core!");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -132,8 +179,12 @@ int SDL_AppInit(int argc, char *argv[]) {
|
||||||
|
|
||||||
mCoreLoadConfig(core);
|
mCoreLoadConfig(core);
|
||||||
|
|
||||||
|
cores[i] = core;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned w, h;
|
unsigned w, h;
|
||||||
core->currentVideoSize(core, &w, &h);
|
// without loss of generality
|
||||||
|
cores[0]->currentVideoSize(cores[0], &w, &h);
|
||||||
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) != 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) != 0) {
|
||||||
SDL_Log("Couldn't initialize SDL3: %s", SDL_GetError());
|
SDL_Log("Couldn't initialize SDL3: %s", SDL_GetError());
|
||||||
|
@ -185,62 +236,77 @@ int SDL_AppInit(int argc, char *argv[]) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_CameraSpec spec = {
|
SDL_CameraSpec init_spec = {
|
||||||
.format = SDL_PIXELFORMAT_XBGR1555,
|
.format = SDL_PIXELFORMAT_XBGR1555,
|
||||||
.width = 320,
|
.width = 320,
|
||||||
.height = 240,
|
.height = 240,
|
||||||
.interval_numerator = 1,
|
.interval_numerator = 1,
|
||||||
.interval_denominator = 30,
|
.interval_denominator = 30,
|
||||||
};
|
};
|
||||||
camera = SDL_OpenCameraDevice(devid, &spec);
|
camera = SDL_OpenCameraDevice(devid, &init_spec);
|
||||||
if (!camera) {
|
if (!camera) {
|
||||||
SDL_Log("Failed to open camera device: %s", SDL_GetError());
|
SDL_Log("Failed to open camera device: %s", SDL_GetError());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if BYTES_PER_PIXEL == 4
|
#if BYTES_PER_PIXEL == 4
|
||||||
screen = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR8888);
|
uint32_t fmt = SDL_PIXELFORMAT_XBGR8888;
|
||||||
#elif BYTES_PER_PIXEL == 2
|
#elif BYTES_PER_PIXEL == 2
|
||||||
screen = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_XBGR1555);
|
uint32_t fmt = SDL_PIXELFORMAT_XBGR1555;
|
||||||
#else
|
#else
|
||||||
#error "unknown pixel format"
|
#error "unknown pixel format"
|
||||||
#endif
|
#endif
|
||||||
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||||
|
int j;
|
||||||
|
struct mCore* core = cores[i];
|
||||||
|
|
||||||
|
SDL_Surface* screen = SDL_CreateSurface(w, h, fmt);
|
||||||
core->setVideoBuffer(core, screen->pixels, screen->pitch / BYTES_PER_PIXEL);
|
core->setVideoBuffer(core, screen->pixels, screen->pitch / BYTES_PER_PIXEL);
|
||||||
|
screens[i] = screen;
|
||||||
|
|
||||||
/* Create texture with appropriate format */
|
/* Create texture with appropriate format */
|
||||||
texture = SDL_CreateTextureFromSurface(renderer, screen);
|
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, screens[0]);
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
SDL_Log("Couldn't create texture: %s", SDL_GetError());
|
SDL_Log("Couldn't create texture: %s", SDL_GetError());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
|
||||||
|
textures[i] = texture;
|
||||||
|
|
||||||
core->setPeripheral(core, mPERIPH_IMAGE_SOURCE, (void*)&gb_img_src);
|
core->setPeripheral(core, mPERIPH_IMAGE_SOURCE, (void*)&gb_img_src[i]);
|
||||||
|
|
||||||
core->reset(core);
|
core->reset(core);
|
||||||
|
|
||||||
core->setKeys(core, 0);
|
core->setKeys(core, 0);
|
||||||
for (i = 0; i < 600; i++) {
|
for (j = 0; j < 600; j++) {
|
||||||
core->runFrame(core);
|
core->runFrame(core);
|
||||||
}
|
}
|
||||||
core->setKeys(core, 1); // title screen
|
core->setKeys(core, 1); // title screen
|
||||||
core->runFrame(core);
|
core->runFrame(core);
|
||||||
core->setKeys(core, 0);
|
core->setKeys(core, 0);
|
||||||
for (i = 0; i < 25; i++) {
|
for (j = 0; j < 25; j++) {
|
||||||
core->runFrame(core);
|
core->runFrame(core);
|
||||||
}
|
}
|
||||||
core->setKeys(core, 1); // select 'shoot'
|
core->setKeys(core, 1); // select 'shoot'
|
||||||
core->runFrame(core);
|
core->runFrame(core);
|
||||||
core->setKeys(core, 0);
|
core->setKeys(core, 0);
|
||||||
for (i = 0; i < 75; i++) {
|
for (j = 0; j < 75; j++) {
|
||||||
core->runFrame(core);
|
core->runFrame(core);
|
||||||
}
|
}
|
||||||
core->setKeys(core, 1); // select 'shoot' again
|
core->setKeys(core, 1); // select 'shoot' again
|
||||||
core->runFrame(core);
|
core->runFrame(core);
|
||||||
core->setKeys(core, 0);
|
core->setKeys(core, 0);
|
||||||
for (i = 0; i < 100; i++) {
|
for (j = 0; j < 100; j++) {
|
||||||
core->runFrame(core);
|
core->runFrame(core);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_Log("prepared core %d", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetTextureColorMod(textures[0], 255, 0, 0);
|
||||||
|
SDL_SetTextureColorMod(textures[1], 0, 255, 0);
|
||||||
|
SDL_SetTextureColorMod(textures[2], 0, 0, 255);
|
||||||
|
|
||||||
return 0; /* start the main app loop. */
|
return 0; /* start the main app loop. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,11 +328,6 @@ static int FlipCamera(void) {
|
||||||
if (nextcam) {
|
if (nextcam) {
|
||||||
SDL_Log("Flip camera!");
|
SDL_Log("Flip camera!");
|
||||||
|
|
||||||
if (frame_current) {
|
|
||||||
SDL_ReleaseCameraFrame(camera, frame_current);
|
|
||||||
frame_current = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_CloseCamera(camera);
|
SDL_CloseCamera(camera);
|
||||||
|
|
||||||
camera = SDL_OpenCameraDevice(nextcam, NULL);
|
camera = SDL_OpenCameraDevice(nextcam, NULL);
|
||||||
|
@ -321,49 +382,77 @@ int SDL_AppEvent(const SDL_Event *event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int SDL_AppIterate(void) {
|
int SDL_AppIterate(void) {
|
||||||
SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255);
|
int i;
|
||||||
SDL_RenderClear(renderer);
|
|
||||||
|
|
||||||
|
if (scaled != NULL) {
|
||||||
Uint64 timestampNS = 0;
|
Uint64 timestampNS = 0;
|
||||||
SDL_Surface *frame_next = camera ? SDL_AcquireCameraFrame(camera, ×tampNS) : NULL;
|
SDL_Surface *frame = camera ? SDL_AcquireCameraFrame(camera, ×tampNS) : NULL;
|
||||||
|
|
||||||
if (frame_next) {
|
if (frame) {
|
||||||
if (frame_current) {
|
//SDL_Log("new frame %d x %d %s", frame->w, frame->h, SDL_GetPixelFormatName(frame->format->format));
|
||||||
if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) {
|
texture_updated = SDL_FALSE;
|
||||||
|
|
||||||
|
struct SDL_Rect srcrect = frame->clip_rect;
|
||||||
|
int w = srcrect.h * scaled->clip_rect.w / scaled->clip_rect.h;
|
||||||
|
if (w >= srcrect.w) {
|
||||||
|
srcrect.x = (srcrect.w - w) / 2;
|
||||||
|
srcrect.w = w;
|
||||||
|
} else {
|
||||||
|
int h = srcrect.w * scaled->clip_rect.h / scaled->clip_rect.w;
|
||||||
|
srcrect.y = (srcrect.h - h) / 2;
|
||||||
|
srcrect.h = h;
|
||||||
|
}
|
||||||
|
int res = SDL_BlitSurfaceScaled(frame, &srcrect, scaled, &scaled->clip_rect, SDL_SCALEMODE_NEAREST);
|
||||||
|
if (res != 0) {
|
||||||
|
SDL_Log("failed to scale: %s", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SDL_ReleaseCameraFrame(camera, frame) < 0) {
|
||||||
SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError());
|
SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_SetSurfaceColorMod(scaled, 255, 0, 0);
|
||||||
|
SDL_BlitSurface(scaled, NULL, filtered[0], NULL);
|
||||||
|
SDL_SetSurfaceColorMod(scaled, 0, 255, 0);
|
||||||
|
SDL_BlitSurface(scaled, NULL, filtered[1], NULL);
|
||||||
|
SDL_SetSurfaceColorMod(scaled, 0, 0, 255);
|
||||||
|
SDL_BlitSurface(scaled, NULL, filtered[2], NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Log("new frame %d x %d %s", frame_next->w, frame_next->h, SDL_GetPixelFormatName(frame_next->format->format));
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||||
|
struct mCore* core = cores[i];
|
||||||
/* It's not needed to keep the frame once updated the texture is updated.
|
|
||||||
* But in case of 0-copy, it's needed to have the frame while using the texture.
|
|
||||||
*/
|
|
||||||
frame_current = frame_next;
|
|
||||||
texture_updated = SDL_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
core->runFrame(core);
|
core->runFrame(core);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!texture_updated) {
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
/* Update SDL_Texture with last video frame (only once per new frame) */
|
/* Update SDL_Texture with last video frame (only once per new frame) */
|
||||||
if (frame_current && !texture_updated) {
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||||
// if (scaled) SDL_UpdateTexture(texture, NULL, scaled->pixels, scaled->pitch);
|
SDL_Surface* screen = screens[i];
|
||||||
|
SDL_Texture* texture = textures[i];
|
||||||
SDL_UpdateTexture(texture, NULL, screen->pixels, screen->pitch);
|
SDL_UpdateTexture(texture, NULL, screen->pixels, screen->pitch);
|
||||||
texture_updated = SDL_TRUE;
|
texture_updated = SDL_TRUE;
|
||||||
|
SDL_RenderTexture(renderer, texture, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_RenderTexture(renderer, texture, NULL, NULL);
|
|
||||||
SDL_RenderPresent(renderer);
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
return 0; /* keep iterating. */
|
return 0; /* keep iterating. */
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDL_AppQuit(void) {
|
void SDL_AppQuit(void) {
|
||||||
mCoreConfigDeinit(&core->config);
|
int i;
|
||||||
core->deinit(core);
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||||
|
mCoreConfigDeinit(&cores[i]->config);
|
||||||
|
cores[i]->deinit(cores[i]);
|
||||||
|
SDL_DestroyTexture(textures[i]);
|
||||||
|
}
|
||||||
|
|
||||||
SDL_ReleaseCameraFrame(camera, frame_current);
|
|
||||||
SDL_CloseCamera(camera);
|
SDL_CloseCamera(camera);
|
||||||
SDL_DestroyTexture(texture);
|
|
||||||
SDL_DestroyRenderer(renderer);
|
SDL_DestroyRenderer(renderer);
|
||||||
SDL_DestroyWindow(window);
|
SDL_DestroyWindow(window);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue