ASW Lib
A.D.S. Games SDL Wrapper Library. A library targeted at Allegro4 users who want to switch to SDL3 and use modern c++.
Loading...
Searching...
No Matches
assets.cpp
Go to the documentation of this file.
2
3#include <SDL3/SDL.h>
4#include <SDL3_image/SDL_image.h>
5#include <SDL3_mixer/SDL_mixer.h>
6#include <SDL3_ttf/SDL_ttf.h>
7#include <algorithm>
8#include <cmath>
9#include <string>
10#include <unordered_map>
11
13#include "./asw/modules/draw.h"
14#include "./asw/modules/sound.h"
15#include "./asw/modules/types.h"
16#include "./asw/modules/util.h"
17
18namespace {
19std::unordered_map<std::string, asw::Texture> textures;
20std::unordered_map<std::string, asw::Font> fonts;
21std::unordered_map<std::string, asw::Sample> samples;
22std::unordered_map<std::string, asw::Music> music;
23} // namespace
24
25// --- Paths ---
26
27std::string asw::assets::get_path(const std::string& filename)
28{
29 // base_path is usually ".../YourGame.app/Contents/Resources/" on mac
30 // simply the directory of the executable on other platforms
31 const char* base_path = SDL_GetBasePath();
32 if (base_path == nullptr) {
33 return filename;
34 }
35 return std::string(base_path) + filename;
36}
37
38std::string asw::assets::get_save_path(const std::string& org, const std::string& app)
39{
40 char* pref_path = SDL_GetPrefPath(org.c_str(), app.c_str());
41 if (pref_path == nullptr) {
42 return "";
43 }
44
45 std::string path(pref_path);
46 SDL_free(pref_path);
47 return path;
48}
49
50// --- Texture ---
51
52asw::Texture asw::assets::load_texture(const std::string& filename)
53{
54 const auto full_path = get_path(filename);
55 SDL_Texture* temp = IMG_LoadTexture(asw::display::get_renderer(), full_path.c_str());
56
57 if (temp == nullptr) {
58 asw::util::abort_on_error("Failed to load texture: " + full_path);
59 }
60
61 SDL_SetTextureScaleMode(temp, SDL_SCALEMODE_NEAREST);
62 SDL_SetTextureBlendMode(temp, SDL_BLENDMODE_BLEND);
63
64 // Guard: if the renderer is already gone when the deleter fires (e.g. a
65 // shared_ptr outliving core::shutdown()), skip the SDL call - SDL has
66 // already freed the texture via SDL_DestroyRenderer.
67 return { temp, [](SDL_Texture* t) {
68 if (asw::display::get_renderer() != nullptr) {
69 SDL_DestroyTexture(t);
70 }
71 } };
72}
73
74asw::Texture asw::assets::load_texture(const std::string& filename, const std::string& key)
75{
76 if (auto it = textures.find(key); it != textures.end()) {
77 return it->second;
78 }
79
80 Texture tex = load_texture(filename);
81 textures.try_emplace(key, tex);
82 return tex;
83}
84
86{
87 auto it = textures.find(key);
88 if (it == textures.end()) {
89 asw::util::abort_on_error("Texture not found: " + key);
90 }
91 return it->second;
92}
93
94void asw::assets::unload_texture(const std::string& key)
95{
96 textures.erase(key);
97}
98
100{
101 auto* r = asw::display::get_renderer();
102 if (r == nullptr) {
103 asw::util::abort_on_error("Renderer not initialized");
104 }
105
106 SDL_Texture* txr
107 = SDL_CreateTexture(r, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
108
109 SDL_SetTextureScaleMode(txr, SDL_SCALEMODE_NEAREST);
110 SDL_SetTextureBlendMode(txr, SDL_BLENDMODE_BLEND);
111
112 return { txr, [](SDL_Texture* t) {
113 if (asw::display::get_renderer() != nullptr) {
114 SDL_DestroyTexture(t);
115 }
116 } };
117}
118
120{
121 auto* r = asw::display::get_renderer();
122 if (r == nullptr) {
123 asw::util::abort_on_error("Renderer not initialized");
124 }
125
126 SDL_Surface* surface = SDL_CreateSurface(size, size, SDL_PIXELFORMAT_RGBA32);
127 if (surface == nullptr) {
128 asw::util::abort_on_error("Failed to create gradient surface");
129 }
130
131 const auto* details = SDL_GetPixelFormatDetails(surface->format);
132 const float half = static_cast<float>(size) / 2.0F;
133
134 auto mix = [](uint8_t a, uint8_t b, float t) {
135 return static_cast<uint8_t>(
136 std::lround(static_cast<float>(a) + (static_cast<float>(b) - a) * t));
137 };
138
139 for (int y = 0; y < size; y++) {
140 auto* row = reinterpret_cast<uint32_t*>(
141 static_cast<uint8_t*>(surface->pixels) + y * surface->pitch);
142
143 for (int x = 0; x < size; x++) {
144 const float distance = std::hypot(static_cast<float>(x) + 0.5F - half,
145 static_cast<float>(y) + 0.5F - half)
146 / half;
147
148 // Smoothstep from the centre to the edge
149 const float t = std::clamp(distance, 0.0F, 1.0F);
150 const float amount = t * t * (3.0F - 2.0F * t);
151
152 row[x] = SDL_MapRGBA(details, nullptr, mix(inner.r, outer.r, amount),
153 mix(inner.g, outer.g, amount), mix(inner.b, outer.b, amount),
154 mix(inner.a, outer.a, amount));
155 }
156 }
157
158 SDL_Texture* txr = SDL_CreateTextureFromSurface(r, surface);
159 SDL_DestroySurface(surface);
160
161 if (txr == nullptr) {
162 asw::util::abort_on_error("Failed to create gradient texture");
163 }
164
165 SDL_SetTextureScaleMode(txr, SDL_SCALEMODE_LINEAR);
166 SDL_SetTextureBlendMode(txr, SDL_BLENDMODE_BLEND);
167
168 return { txr, [](SDL_Texture* t) {
169 if (asw::display::get_renderer() != nullptr) {
170 SDL_DestroyTexture(t);
171 }
172 } };
173}
174
175// --- Font ---
176
177asw::Font asw::assets::load_font(const std::string& filename, float size, asw::FontStyle style)
178{
179 const auto full_path = get_path(filename);
180 TTF_Font* temp = TTF_OpenFont(full_path.c_str(), size);
181
182 if (temp == nullptr) {
183 asw::util::abort_on_error("Failed to load font: " + full_path);
184 }
185
186 // Mono hinting renders glyphs without anti-aliasing. draw::text reads it
187 // back to pick nearest filtering.
188 if (style == asw::FontStyle::Pixel) {
189 TTF_SetFontHinting(temp, TTF_HINTING_MONO);
190 }
191
192 // Only close while SDL_ttf is running. Checking the renderer instead
193 // leaked every font in OpenGL mode, which has no renderer.
194 return { temp, [](TTF_Font* f) {
195 if (TTF_WasInit() > 0) {
196 TTF_CloseFont(f);
197 }
198 } };
199}
200
202 const std::string& filename, float size, const std::string& key, asw::FontStyle style)
203{
204 if (auto it = fonts.find(key); it != fonts.end()) {
205 return it->second;
206 }
207
208 Font font = load_font(filename, size, style);
209 fonts.try_emplace(key, font);
210 return font;
211}
212
213asw::Font asw::assets::get_font(const std::string& key)
214{
215 auto it = fonts.find(key);
216 if (it == fonts.end()) {
217 asw::util::abort_on_error("Font not found: " + key);
218 }
219 return it->second;
220}
221
222void asw::assets::unload_font(const std::string& key)
223{
224 fonts.erase(key);
227}
228
229// --- Sample ---
230
231asw::Sample asw::assets::load_sample(const std::string& filename)
232{
233 const auto full_path = get_path(filename);
234 MIX_Audio* temp = MIX_LoadAudio(asw::sound::get_mixer(), full_path.c_str(), true);
235
236 if (temp == nullptr) {
237 asw::util::abort_on_error("Failed to load sample: " + full_path);
238 }
239
240 return { temp, [](MIX_Audio* a) {
241 if (asw::sound::get_mixer() != nullptr) {
242 MIX_DestroyAudio(a);
243 }
244 } };
245}
246
247asw::Sample asw::assets::load_sample(const std::string& filename, const std::string& key)
248{
249 if (auto it = samples.find(key); it != samples.end()) {
250 return it->second;
251 }
252
253 Sample sample = load_sample(filename);
254 samples.try_emplace(key, sample);
255 return sample;
256}
257
259{
260 auto it = samples.find(key);
261 if (it == samples.end()) {
262 asw::util::abort_on_error("Sample not found: " + key);
263 }
264 return it->second;
265}
266
267void asw::assets::unload_sample(const std::string& key)
268{
269 samples.erase(key);
270}
271
272// --- Music ---
273
274asw::Music asw::assets::load_music(const std::string& filename)
275{
276 const auto full_path = get_path(filename);
277 MIX_Audio* temp = MIX_LoadAudio(asw::sound::get_mixer(), full_path.c_str(), false);
278
279 if (temp == nullptr) {
280 asw::util::abort_on_error("Failed to load music: " + full_path);
281 }
282
283 return { temp, [](MIX_Audio* a) {
284 if (asw::sound::get_mixer() != nullptr) {
285 MIX_DestroyAudio(a);
286 }
287 } };
288}
289
290asw::Music asw::assets::load_music(const std::string& filename, const std::string& key)
291{
292 if (auto it = music.find(key); it != music.end()) {
293 return it->second;
294 }
295
296 Music mus = load_music(filename);
297 music.try_emplace(key, mus);
298 return mus;
299}
300
301asw::Music asw::assets::get_music(const std::string& key)
302{
303 auto it = music.find(key);
304 if (it == music.end()) {
305 asw::util::abort_on_error("Music not found: " + key);
306 }
307 return it->second;
308}
309
310void asw::assets::unload_music(const std::string& key)
311{
312 music.erase(key);
313}
314
315// --- Global ---
316
318{
321 textures.clear();
322 fonts.clear();
323 samples.clear();
324 music.clear();
325}
Asset routines for the ASW library.
Display and window routines for the ASW library.
Routines for drawing sprites and primitives to the screen.
std::unordered_map< std::string, asw::Music > music
Definition assets.cpp:22
std::unordered_map< std::string, asw::Sample > samples
Definition assets.cpp:21
std::unordered_map< std::string, asw::Texture > textures
Definition assets.cpp:19
std::unordered_map< std::string, asw::Font > fonts
Definition assets.cpp:20
asw::Sample get_sample(const std::string &key)
Get a cached sample.
Definition assets.cpp:258
asw::Texture create_texture(int w, int h)
Create a Texture given the specified dimensions.
Definition assets.cpp:99
void unload_music(const std::string &key)
Remove a cached music.
Definition assets.cpp:310
std::string get_path(const std::string &filename)
Get the full path to an asset given its filename. The filename is relative to the base path of the ap...
Definition assets.cpp:27
asw::Texture create_radial_gradient(int size, asw::Color inner, asw::Color outer)
Create a square texture with a radial gradient. The colour goes smoothly from inner at the centre to ...
Definition assets.cpp:119
asw::Texture get_texture(const std::string &key)
Get a cached texture.
Definition assets.cpp:85
void unload_sample(const std::string &key)
Remove a cached sample.
Definition assets.cpp:267
std::string get_save_path(const std::string &org, const std::string &app)
Get a writable folder for save files and settings, unique to the organisation and application....
Definition assets.cpp:38
void unload_font(const std::string &key)
Remove a cached font.
Definition assets.cpp:222
asw::Music load_music(const std::string &filename)
Loads a music file from a file. Formats supported are WAV, AIFF, RIFF, OGG and VOC....
Definition assets.cpp:274
asw::Font load_font(const std::string &filename, float size, asw::FontStyle style=asw::FontStyle::Smooth)
Loads a TTF font from a file. This will abort if the file is not found.
Definition assets.cpp:177
void clear_all()
Clear all cached assets. This will remove all cached textures, fonts, samples, and music.
Definition assets.cpp:317
void unload_texture(const std::string &key)
Remove a cached texture.
Definition assets.cpp:94
asw::Music get_music(const std::string &key)
Get a cached music.
Definition assets.cpp:301
asw::Sample load_sample(const std::string &filename)
Loads a sample from a file. Formats supported are WAV, AIFF, RIFF, OGG and VOC. This will abort if th...
Definition assets.cpp:231
asw::Font get_font(const std::string &key)
Get a cached font.
Definition assets.cpp:213
asw::Texture load_texture(const std::string &filename)
Loads a texture from a file. Formats supported are PNG, ICO, CUR, BMP, GIF, JPG, LBM,...
Definition assets.cpp:52
asw::Renderer * get_renderer()
Get the SDL renderer.
Definition display.cpp:93
void clear_text_cache()
Clear cached rendered text textures.
Definition draw.cpp:457
MIX_Mixer * get_mixer()
Get the SDL mixer device.
Definition sound.cpp:391
void clear_text_size_cache()
Clear cached text metrics.
Definition util.cpp:85
void abort_on_error(const std::string &message)
Abort program and display error message.
Definition util.cpp:36
std::shared_ptr< TTF_Font > Font
Alias for a shared pointer to an TTF_Font.
Definition types.h:67
std::shared_ptr< MIX_Audio > Sample
Alias for a shared pointer to an MIX_Audio.
Definition types.h:70
std::shared_ptr< SDL_Texture > Texture
Alias for a shared pointer to an SDL_Texture.
Definition types.h:64
std::shared_ptr< MIX_Audio > Music
Alias for a shared pointer to an MIX_Audio.
Definition types.h:73
FontStyle
How a font's glyphs are rendered.
Definition types.h:53
Sound module for the ASW library.
RGBA color struct with 8-bit channels.
Definition color.h:14
uint8_t b
Definition color.h:28
uint8_t r
Definition color.h:26
uint8_t g
Definition color.h:27
uint8_t a
Definition color.h:29
Types used throughout the ASW library.
General utility functions.