#include "graphics.h" #include "beatmapselector.h" #include "defines.h" #include "util.h" #include #include #include #include #include struct Osu_Graphics* Osu_CreateGraphics(const char* dataPath) { SDL_SAFECALL(SDL_Init, SDL_INIT_VIDEO); struct Osu_Graphics* graphics = calloc(1, sizeof(struct Osu_Graphics)); SDL_SAFECALL(SDL_CreateWindowAndRenderer, "osu! " OSU_VERSION, 800, 600, SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_RESIZABLE | SDL_WINDOW_FULLSCREEN, &graphics->window, &graphics->renderer) // TODO: Go through all calls and check which actually need to be fatal errors. SDL_SAFECALL(SDL_SetRenderVSync, graphics->renderer, true); SDL_SAFECALL(SDL_HideCursor); char path[512]; snprintf(path, 512, "%s" OSU_PATHSEP "assets" OSU_PATHSEP "Inter-Medium.ttf", dataPath); SDL_SAFECALL(TTF_Init); SDL_SAFEASSN(graphics->font, TTF_OpenFont, path, 32); SDL_SAFEASSN(graphics->debugFont, TTF_OpenFont, path, 16); SDL_SAFEASSN(graphics->textEngine, TTF_CreateRendererTextEngine, graphics->renderer); SDL_SAFEASSN(graphics->debugText, TTF_CreateText, graphics->textEngine, graphics->debugFont, "", 0); // TODO: There is a lot of conditional logic regarding skins that we are outright skipping. snprintf(path, 512, "%s" OSU_PATHSEP "skins" OSU_PATHSEP OSU_ACTIVE_SKIN OSU_PATHSEP "%s", dataPath, "menu-button-background@2x.png"); SDL_SAFEASSN(graphics->beatmapWedgeTexture, IMG_LoadTexture, graphics->renderer, path); snprintf(path, 512, "%s" OSU_PATHSEP "skins" OSU_PATHSEP OSU_ACTIVE_SKIN OSU_PATHSEP "%s", dataPath, "cursor.png"); SDL_SAFEASSN(graphics->cursorTexture, IMG_LoadTexture, graphics->renderer, path); snprintf(path, 512, "%s" OSU_PATHSEP "skins" OSU_PATHSEP OSU_ACTIVE_SKIN OSU_PATHSEP "%s", dataPath, "menu-background.png"); SDL_SAFEASSN(graphics->menuBackgroundTexture, IMG_LoadTexture, graphics->renderer, path); snprintf(path, 512, "%s" OSU_PATHSEP "skins" OSU_PATHSEP OSU_ACTIVE_SKIN OSU_PATHSEP "%s", dataPath, "songselect-top.png"); SDL_SAFEASSN(graphics->songSelectTopTexture, IMG_LoadTexture, graphics->renderer, path); snprintf(path, 512, "%s" OSU_PATHSEP "skins" OSU_PATHSEP OSU_ACTIVE_SKIN OSU_PATHSEP "%s", dataPath, "hitcircle.png"); SDL_SAFEASSN(graphics->hitCircleTexture, IMG_LoadTexture, graphics->renderer, path); snprintf(path, 512, "%s" OSU_PATHSEP "skins" OSU_PATHSEP OSU_ACTIVE_SKIN OSU_PATHSEP "%s", dataPath, "hitcircleoverlay.png"); SDL_SAFEASSN(graphics->hitCircleOverlayTexture, IMG_LoadTexture, graphics->renderer, path); snprintf(path, 512, "%s" OSU_PATHSEP "skins" OSU_PATHSEP OSU_ACTIVE_SKIN OSU_PATHSEP "%s", dataPath, "approachcircle.png"); SDL_SAFEASSN(graphics->approachCircleTexture, IMG_LoadTexture, graphics->renderer, path); graphics->beatmapDimFactor = 255; return graphics; } void Osu_DestroyGraphics(struct Osu_Graphics* graphics) { if (graphics->selectedBeatmapWallpaper) { SDL_DestroyTexture(graphics->selectedBeatmapWallpaper); } SDL_DestroyTexture(graphics->beatmapWedgeTexture); SDL_DestroyTexture(graphics->cursorTexture); SDL_DestroyTexture(graphics->menuBackgroundTexture); SDL_DestroyTexture(graphics->songSelectTopTexture); SDL_DestroyTexture(graphics->hitCircleTexture); SDL_DestroyTexture(graphics->hitCircleOverlayTexture); SDL_DestroyTexture(graphics->approachCircleTexture); TTF_DestroyText(graphics->debugText); graphics->debugText = NULL; TTF_DestroyRendererTextEngine(graphics->textEngine); TTF_CloseFont(graphics->debugFont); TTF_CloseFont(graphics->font); TTF_Quit(); SDL_DestroyRenderer(graphics->renderer); SDL_DestroyWindow(graphics->window); SDL_QuitSubSystem(SDL_INIT_VIDEO); free(graphics); } void Osu_PrepareRender(struct Osu_Graphics* graphics) { int screenW, screenH; SDL_SAFECALL(SDL_GetWindowSizeInPixels, graphics->window, &screenW, &screenH); graphics->screenRect.w = screenW; graphics->screenRect.h = screenH; SDL_SAFECALL(SDL_SetRenderDrawColor, graphics->renderer, 0, 0, 0, 255); SDL_SAFECALL(SDL_RenderClear, graphics->renderer); } void Osu_RenderWallpaper(struct Osu_Graphics* graphics) { struct SDL_Texture* wallpaper = graphics->menuBackgroundTexture; if (graphics->selectedBeatmapWallpaper) { wallpaper = graphics->selectedBeatmapWallpaper; } float texW, texH; SDL_SAFECALL(SDL_GetTextureSize, wallpaper, &texW, &texH); SDL_assert(texW > 0); SDL_assert(texH > 0); double texAspectRatio = texW / texH; double screenAspectRatio = graphics->screenRect.w / graphics->screenRect.h; double scalingFactor = 1.0; if (screenAspectRatio > texAspectRatio) { scalingFactor = graphics->screenRect.w / texW; } else { scalingFactor = graphics->screenRect.h / texH; } float adjustedTexW = texW * scalingFactor; float adjustedTexH = texH * scalingFactor; SDL_FRect destRect = { .x = (graphics->screenRect.w / 2) - (adjustedTexW / 2), .y = (graphics->screenRect.h / 2) - (adjustedTexH / 2), .w = adjustedTexW, .h = adjustedTexH, }; SDL_SAFECALL(SDL_RenderTexture, graphics->renderer, wallpaper, NULL, &destRect); } void Osu_RenderCursor(struct Osu_Graphics* graphics) { struct SDL_FRect texRect = Osu_GetTextureRect(graphics->cursorTexture); texRect.w *= 1.25; texRect.h *= 1.25; float mouseX, mouseY; SDL_GetMouseState(&mouseX, &mouseY); float scale; SDL_SAFEASSN(scale, SDL_GetWindowDisplayScale, graphics->window); mouseX *= scale; mouseY *= scale; SDL_FRect destRect = { .x = mouseX - (texRect.w / 2), .y = mouseY - (texRect.h / 2), .w = texRect.w, .h = texRect.h, }; SDL_SAFECALL(SDL_RenderTexture, graphics->renderer, graphics->cursorTexture, &texRect, &destRect); } void Osu_SetWallpaper(struct Osu_Graphics* graphics, const char* filePath) { if (graphics->selectedBeatmapWallpaper) { SDL_DestroyTexture(graphics->selectedBeatmapWallpaper); } SDL_SAFEASSN(graphics->selectedBeatmapWallpaper, IMG_LoadTexture, graphics->renderer, filePath); }