FifeGUI 0.2.0
A C++ GUI library designed for games.
backends/sdl3/image.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// Corresponding header include
6#include "fifechan/backends/sdl3/image.hpp"
7
8// Standard library includes
9#include <string>
10
11// Third-party library includes
12#include <SDL3/SDL.h>
13#include <SDL3_image/SDL_image.h>
14
15// Project headers (subdirs before local)
16#include "fifechan/backends/sdl3/imageloader.hpp"
17#include "fifechan/exception.hpp"
18
19namespace fcn::sdl3
20{
21 Image::Image(SDL_Surface* surface, bool autoFree, SDL_Renderer* renderer) : mAutoFree(autoFree), mRenderer(renderer)
22 {
23 if (renderer != nullptr && surface != nullptr) {
24
25 // Color Key to Alpha Conversion
26 //
27 // Convert magenta (255,0,255) color key to proper alpha channel.
28 // Why? SDL_CreateTextureFromSurface doesn't automatically convert
29 // color-keyed surfaces to alpha transparency.
30
31 // The color key exists. Disable RLE first to access raw pixels.
32 SDL_SetSurfaceRLE(surface, false);
33
34 if (SDL_GetPixelFormatDetails(surface->format)->bits_per_pixel != 32) {
35 SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA8888);
36 if (converted != nullptr) {
37 if (autoFree) {
38 SDL_DestroySurface(surface);
39 }
40 surface = converted;
41 }
42 }
43
44 // Set magenta as color key only if not already set by ImageLoader
45 Uint32 existingKey = 0;
46 if (SDL_GetSurfaceColorKey(surface, &existingKey) == false) {
47 // Color key already set - preserve it (from ImageLoader)
48 } else {
49 // No color key set - don't add one in constructor (for font images)
50 }
51
52 mTexture = SDL_CreateTextureFromSurface(renderer, surface);
53 if (mTexture == nullptr) {
54 throwException(std::string("Failed to create texture: ") + SDL_GetError());
55 }
56
57 SDL_SetTextureBlendMode(mTexture, SDL_BLENDMODE_BLEND);
58 SDL_SetTextureScaleMode(mTexture, SDL_SCALEMODE_NEAREST);
59
60 float wFloat = 0;
61 float hFloat = 0;
62 SDL_GetTextureSize(mTexture, &wFloat, &hFloat);
63
64 int const w = static_cast<int>(wFloat);
65 int const h = static_cast<int>(hFloat);
66
67 mTransientSurface = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_RGBA8888);
68
69 if (mTransientSurface == nullptr) {
70 throwException(std::string("Failed to create transient surface: ") + SDL_GetError());
71 }
72
73 if (mTransientSurface->format != surface->format) {
74 SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA8888);
75 if (converted != nullptr) {
76 SDL_BlitSurface(converted, nullptr, mTransientSurface, nullptr);
77 SDL_DestroySurface(converted);
78 }
79 } else {
80 SDL_BlitSurface(surface, nullptr, mTransientSurface, nullptr);
81 }
82
83 Uint32 transientKey = 0;
84 if (SDL_GetSurfaceColorKey(mTransientSurface, &transientKey) == false) {
85 // Color key already set - preserve it (from ImageLoader)
86 } else {
87 // No color key set - don't add one (for font images)
88 }
89 SDL_SetSurfaceRLE(mTransientSurface, false);
90
91 if (autoFree) {
92 SDL_DestroySurface(surface);
93 }
94 } else if (autoFree && surface != nullptr) {
95 SDL_DestroySurface(surface);
96 }
97 }
98
99 Image::~Image()
100 {
101 if (mAutoFree) {
102 SDL_DestroySurface(mTransientSurface);
103 mTransientSurface = nullptr;
104 SDL_DestroyTexture(mTexture);
105 mTexture = nullptr;
106 }
107 }
108
109 SDL_Surface* Image::getSurface() const
110 {
111 return mTransientSurface;
112 }
113
114 SDL_Texture* Image::getTexture() const
115 {
116 return mTexture;
117 }
118
119 int Image::getWidth() const
120 {
121 if (mTexture == nullptr) {
122 throwException("Trying to get the width of a non loaded image.");
123 }
124
125 float wFloat = 0;
126 float hFloat = 0;
127 SDL_GetTextureSize(mTexture, &wFloat, &hFloat);
128
129 return static_cast<int>(wFloat);
130 }
131
133 {
134 if (mTexture == nullptr) {
135 throwException("Trying to get the height of a non loaded image.");
136 }
137
138 float wFloat = 0;
139 float hFloat = 0;
140 SDL_GetTextureSize(mTexture, &wFloat, &hFloat);
141
142 return static_cast<int>(hFloat);
143 }
144
145 Color Image::getPixel(int x, int y)
146 {
147 if (mTransientSurface == nullptr) {
148 throwException("Trying to get a pixel from a non loaded image.");
149 }
150
151 unsigned char r = 0;
152 unsigned char g = 0;
153 unsigned char b = 0;
154 unsigned char a = 0;
155 SDL_ReadSurfacePixel(mTransientSurface, x, y, &r, &g, &b, &a);
156
157 return {r, g, b, a};
158 }
159
160 void Image::putPixel(int x, int y, Color const & color)
161 {
162 if (mTransientSurface == nullptr) {
163 throwException("Trying to put a pixel in a non loaded image.");
164 }
165
166 SDL_WriteSurfacePixel(mTransientSurface, x, y, color.r, color.g, color.b, color.a);
167 }
168
170 {
171 if (mTexture == nullptr) {
172 throwException("Trying to convert a non loaded image to display format.");
173 }
174
175 // Regenerate the texture from the transient surface which has the final pixel data.
176 // This ensures the texture reflects any changes made via getPixel/putPixel.
177 if (mTransientSurface != nullptr && mRenderer != nullptr) {
178 SDL_DestroyTexture(mTexture);
179 mTexture = SDL_CreateTextureFromSurface(mRenderer, mTransientSurface);
180 if (mTexture == nullptr) {
181 throwException(std::string("Failed to create texture in convertToDisplayFormat: ") + SDL_GetError());
182 }
183 SDL_SetTextureBlendMode(mTexture, SDL_BLENDMODE_BLEND);
184 SDL_SetTextureScaleMode(mTexture, SDL_SCALEMODE_NEAREST);
185 }
186 }
187
189 {
190 SDL_DestroySurface(mTransientSurface);
191 mTransientSurface = nullptr;
192 SDL_DestroyTexture(mTexture);
193 mTexture = nullptr;
194 }
195} // namespace fcn::sdl3
Color.
Definition color.hpp:58
uint8_t a
Alpha color component (0-255).
Definition color.hpp:350
uint8_t b
Blue color component (0-255).
Definition color.hpp:347
uint8_t g
Green color component (0-255).
Definition color.hpp:344
uint8_t r
Red color component (0-255).
Definition color.hpp:341
void convertToDisplayFormat() override
Converts the image, if possible, to display format.
Color getPixel(int x, int y) override
Gets the color of a pixel at coordinate (x, y) in the image.
virtual SDL_Surface * getSurface() const
Gets the SDL surface for the image.
Image(SDL_Surface *surface, bool autoFree, SDL_Renderer *renderer=nullptr)
Constructor.
int getWidth() const override
Gets the width of the image.
SDL_Texture * mTexture
SDL texture for accelerated rendering (primary storage).
int getHeight() const override
Gets the height of the image.
bool mAutoFree
Whether the transient surface should be freed on destruction.
SDL_Surface * mTransientSurface
Transient surface for pixel operations (getPixel/putPixel).
void putPixel(int x, int y, Color const &color) override
Puts a pixel with a certain color at coordinate (x, y).
void free() override
Frees an image.
virtual SDL_Texture * getTexture() const
Gets the SDL texture for the image.
SDL_Renderer * mRenderer
SDL renderer associated with the texture.
void throwException(std::string const &message, std::source_location location=std::source_location::current())
Throw an Exception capturing the current source location.