FifeGUI 0.3.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 // SDL_GetSurfaceColorKey returns 0 on success (key already set) - just check it.
46 Uint32 existingKey = 0;
47 SDL_GetSurfaceColorKey(surface, &existingKey);
48
49 mTexture = SDL_CreateTextureFromSurface(renderer, surface);
50 if (mTexture == nullptr) {
51 throwException(std::string("Failed to create texture: ") + SDL_GetError());
52 }
53
54 SDL_SetTextureBlendMode(mTexture, SDL_BLENDMODE_BLEND);
55 SDL_SetTextureScaleMode(mTexture, SDL_SCALEMODE_NEAREST);
56
57 float wFloat = 0;
58 float hFloat = 0;
59 SDL_GetTextureSize(mTexture, &wFloat, &hFloat);
60
61 int const w = static_cast<int>(wFloat);
62 int const h = static_cast<int>(hFloat);
63
64 mTransientSurface = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_RGBA8888);
65
66 if (mTransientSurface == nullptr) {
67 throwException(std::string("Failed to create transient surface: ") + SDL_GetError());
68 }
69
70 if (mTransientSurface->format != surface->format) {
71 SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA8888);
72 if (converted != nullptr) {
73 SDL_BlitSurface(converted, nullptr, mTransientSurface, nullptr);
74 SDL_DestroySurface(converted);
75 }
76 } else {
77 SDL_BlitSurface(surface, nullptr, mTransientSurface, nullptr);
78 }
79
80 Uint32 transientKey = 0;
81 SDL_GetSurfaceColorKey(mTransientSurface, &transientKey);
82 SDL_SetSurfaceRLE(mTransientSurface, false);
83
84 if (autoFree) {
85 SDL_DestroySurface(surface);
86 }
87 } else if (autoFree && surface != nullptr) {
88 SDL_DestroySurface(surface);
89 }
90 }
91
92 Image::~Image()
93 {
94 if (mAutoFree) {
95 SDL_DestroySurface(mTransientSurface);
96 mTransientSurface = nullptr;
97 SDL_DestroyTexture(mTexture);
98 mTexture = nullptr;
99 }
100 }
101
102 SDL_Surface* Image::getSurface() const
103 {
104 return mTransientSurface;
105 }
106
107 SDL_Texture* Image::getTexture() const
108 {
109 return mTexture;
110 }
111
112 int Image::getWidth() const
113 {
114 if (mTexture == nullptr) {
115 throwException("Trying to get the width of a non loaded image.");
116 }
117
118 float wFloat = 0;
119 float hFloat = 0;
120 SDL_GetTextureSize(mTexture, &wFloat, &hFloat);
121
122 return static_cast<int>(wFloat);
123 }
124
126 {
127 if (mTexture == nullptr) {
128 throwException("Trying to get the height of a non loaded image.");
129 }
130
131 float wFloat = 0;
132 float hFloat = 0;
133 SDL_GetTextureSize(mTexture, &wFloat, &hFloat);
134
135 return static_cast<int>(hFloat);
136 }
137
138 Color Image::getPixel(int x, int y)
139 {
140 if (mTransientSurface == nullptr) {
141 throwException("Trying to get a pixel from a non loaded image.");
142 }
143
144 unsigned char r = 0;
145 unsigned char g = 0;
146 unsigned char b = 0;
147 unsigned char a = 0;
148 SDL_ReadSurfacePixel(mTransientSurface, x, y, &r, &g, &b, &a);
149
150 return {r, g, b, a};
151 }
152
153 void Image::putPixel(int x, int y, Color const & color)
154 {
155 if (mTransientSurface == nullptr) {
156 throwException("Trying to put a pixel in a non loaded image.");
157 }
158
159 SDL_WriteSurfacePixel(mTransientSurface, x, y, color.r, color.g, color.b, color.a);
160 }
161
163 {
164 if (mTexture == nullptr) {
165 throwException("Trying to convert a non loaded image to display format.");
166 }
167
168 // Regenerate the texture from the transient surface which has the final pixel data.
169 // This ensures the texture reflects any changes made via getPixel/putPixel.
170 if (mTransientSurface != nullptr && mRenderer != nullptr) {
171 SDL_DestroyTexture(mTexture);
172 mTexture = SDL_CreateTextureFromSurface(mRenderer, mTransientSurface);
173 if (mTexture == nullptr) {
174 throwException(std::string("Failed to create texture in convertToDisplayFormat: ") + SDL_GetError());
175 }
176 SDL_SetTextureBlendMode(mTexture, SDL_BLENDMODE_BLEND);
177 SDL_SetTextureScaleMode(mTexture, SDL_SCALEMODE_NEAREST);
178 }
179 }
180
182 {
183 SDL_DestroySurface(mTransientSurface);
184 mTransientSurface = nullptr;
185 SDL_DestroyTexture(mTexture);
186 mTexture = nullptr;
187 }
188} // 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.