FifeGUI 0.2.0
A C++ GUI library designed for games.
imageloader.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2004 - 2008 Olof Naessén and Per Larsson
3// SPDX-FileCopyrightText: 2013 - 2026 Fifengine contributors
4
5#include "fifechan/backends/sdl2/imageloader.hpp"
6
7#include <filesystem>
8#include <string>
9
10#include "SDL_image.h"
11#include "fifechan/backends/sdl2/image.hpp"
12#include "fifechan/exception.hpp"
13
14namespace fcn::sdl2
15{
16 namespace
17 {
18 std::string resolveFromExecutableDirectory(std::string const & filename)
19 {
20 std::filesystem::path const requestedPath(filename);
21 if (requestedPath.is_absolute()) {
22 return filename;
23 }
24
25 char* basePathRaw = SDL_GetBasePath();
26 if (basePathRaw == nullptr) {
27 return filename;
28 }
29
30 std::filesystem::path const candidate = std::filesystem::path(basePathRaw) / requestedPath;
31 SDL_free(basePathRaw);
32 return candidate.string();
33 }
34 } // namespace
35
36 fcn::Image* ImageLoader::load(std::string const & filename, bool convertToDisplayFormat)
37 {
38 SDL_Surface* loadedSurface = loadSDLSurface(filename);
39
40 if (loadedSurface == nullptr) {
41 throwException(std::string("Unable to load image file: ") + filename);
42 }
43
44 SDL_Surface* surface = convertToStandardFormat(loadedSurface);
45 SDL_FreeSurface(loadedSurface);
46
47 if (surface == nullptr) {
48 throwException((std::string("Not enough memory to load: ") + filename));
49 }
50
51 Image* image = new Image(surface, true, mRenderer);
52
53 if (convertToDisplayFormat) {
55 }
56
57 return image;
58 }
59
60 void ImageLoader::setRenderer(SDL_Renderer* renderer)
61 {
62 mRenderer = renderer;
63 }
64
65 SDL_Surface* ImageLoader::loadSDLSurface(std::string const & filename)
66 {
67 SDL_Surface* surface = IMG_Load(filename.c_str());
68 if (surface != nullptr) {
69 return surface;
70 }
71
72 std::string const resolvedPath = resolveFromExecutableDirectory(filename);
73 if (resolvedPath == filename) {
74 return nullptr;
75 }
76
77 return IMG_Load(resolvedPath.c_str());
78 }
79
80 SDL_Texture* ImageLoader::loadSDLTexture(std::string const & filename)
81 {
82 SDL_Texture* texture = IMG_LoadTexture(mRenderer, filename.c_str());
83 if (texture != nullptr) {
84 return texture;
85 }
86
87 std::string const resolvedPath = resolveFromExecutableDirectory(filename);
88 if (resolvedPath == filename) {
89 return nullptr;
90 }
91
92 return IMG_LoadTexture(mRenderer, resolvedPath.c_str());
93 }
94
95 SDL_Surface* ImageLoader::convertToStandardFormat(SDL_Surface* surface)
96 {
97 if (surface == nullptr) {
98 return nullptr;
99 }
100
101 bool hasPink = false;
102
103 int const pixels = surface->w * surface->h;
104
105 for (int i = 0; i < pixels; ++i) {
106 uint8_t r{};
107 uint8_t g{};
108 uint8_t b{};
109 uint8_t a{};
110
111 SDL_GetRGBA(reinterpret_cast<Uint32*>(surface->pixels)[i], surface->format, &r, &g, &b, &a);
112
113 if (r == 255 && g == 0 && b == 255) {
114 hasPink = true;
115 }
116 }
117
118 // SDL interprets each pixel as a 32-bit number.
119 // We need to mask depending on the endianness (byte order) of the machine.
120 // Rmask being 0xFF000000 means the red data is stored in the most significant byte
121#if SDL_BYTEORDER == SDL_BIG_ENDIAN
122 uint32_t rmask = 0xff000000;
123 uint32_t gmask = 0x00ff0000;
124 uint32_t bmask = 0x0000ff00;
125 uint32_t amask = 0x000000ff;
126#else
127 uint32_t const rmask = 0x000000ff;
128 uint32_t const gmask = 0x0000ff00;
129 uint32_t const bmask = 0x00ff0000;
130 uint32_t const amask = 0xff000000;
131#endif
132
133 // Create a 32-bit color surface to standardize format
134 auto* targetFormatSurface = SDL_CreateRGBSurface(0, surface->w, surface->h, 32, rmask, gmask, bmask, amask);
135
136 if (targetFormatSurface == nullptr) {
137 return nullptr;
138 }
139
140 // Convert the original surface to the standard format
141 auto* converted = SDL_ConvertSurface(surface, targetFormatSurface->format, 0);
142 SDL_FreeSurface(targetFormatSurface);
143
144 if (converted == nullptr) {
145 return nullptr;
146 }
147
148 if (hasPink) {
149 SDL_SetColorKey(converted, SDL_TRUE, SDL_MapRGB(converted->format, 255, 0, 255));
150 SDL_SetSurfaceRLE(converted, 1);
151 }
152
153 return converted;
154 }
155
156 SDL_PixelFormat const & ImageLoader::getSDLPixelFormat()
157 {
158 return mPixelFormat;
159 }
160
161 void ImageLoader::setSDLPixelFormat(SDL_PixelFormat const & format)
162 {
163 mPixelFormat = format;
164 }
165} // namespace fcn::sdl2
Abstract holder for image data.
Definition image.hpp:32
virtual SDL_Texture * loadSDLTexture(std::string const &filename)
Load an SDL_Texture from disk (internal).
virtual SDL_Surface * convertToStandardFormat(SDL_Surface *surface)
Convert a surface to a standard internal format (internal).
fcn::Image * load(std::string const &filename, bool convertToDisplayFormat) override
Load an image from filename.
virtual SDL_Surface * loadSDLSurface(std::string const &filename)
Load an SDL_Surface from disk (internal).
void setRenderer(SDL_Renderer *renderer)
Set the SDL renderer used when creating textures.
SDL_PixelFormat const & getSDLPixelFormat()
Return the current SDL pixel format used for conversions.
void setSDLPixelFormat(SDL_PixelFormat const &format)
Set the SDL pixel format used for conversions.
SDL2-specific implementation of Image.
void convertToDisplayFormat() override
Converts the image, if possible, to display format.
Unified header for the SDL backend.
fcn::sdl2::Image Image
Backend alias for the SDL image implementation.
Definition sdl.hpp:28