FifeGUI 0.2.0
A C++ GUI library designed for games.
backends/sdl2/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#include "fifechan/backends/sdl2/image.hpp"
6
7#include <SDL2/SDL_image.h>
8#include <SDL2/SDL_render.h>
9
10#include <string>
11
12#include "fifechan/backends/sdl2/imageloader.hpp"
13#include "fifechan/backends/sdl2/pixel.hpp"
14#include "fifechan/exception.hpp"
15
16namespace fcn::sdl2
17{
18 Image::Image(SDL_Surface* surface, bool autoFree, SDL_Renderer* renderer) : mAutoFree(autoFree), mRenderer(renderer)
19 {
20 if (renderer != nullptr && surface != nullptr) {
21
22 // Color Key to Alpha Conversion
23 //
24 // Convert magenta (255,0,255) color key to proper alpha channel.
25 // Why? SDL_CreateTextureFromSurface doesn't automatically convert
26 // color-keyed surfaces to alpha transparency.
27
28 // The color key exists. Disable RLE first to access raw pixels.
29 SDL_SetSurfaceRLE(surface, 0);
30
31 SDL_Surface* workingSurface = surface;
32
33 if (surface->format->BitsPerPixel != 32) {
34 workingSurface = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA8888, 0);
35 if (workingSurface != nullptr) {
36 if (autoFree) {
37 SDL_FreeSurface(surface);
38 }
39 surface = workingSurface;
40 }
41 }
42
43 if (SDL_LockSurface(surface) == 0) {
44 Uint32* pixels = static_cast<Uint32*>(surface->pixels);
45 int const pixelCount = surface->w * surface->h;
46 // Get format info for RGBA extraction
47 SDL_PixelFormat const * fmt = surface->format;
48 Uint32 const amask = fmt->Amask;
49 // Extract RGBA, check for magenta and set alpha to 0 if found.
50 for (int i = 0; i < pixelCount; ++i) {
51 // Extract RGBA
52 Uint8 r, g, b, a;
53 SDL_GetRGBA(pixels[i], fmt, &r, &g, &b, &a);
54
55 if (r == 255 && g == 0 && b == 255) {
56 pixels[i] &= ~amask;
57 }
58 }
59 SDL_UnlockSurface(surface);
60 }
61 // Clear color key as alpha is now used for transparency.
62 SDL_SetColorKey(surface, 0, 0);
63 // --- End Color Key Conversion ---
64 mTexture = SDL_CreateTextureFromSurface(renderer, surface);
65 if (mTexture == nullptr) {
66 throwException(std::string("Failed to create texture: ") + SDL_GetError());
67 }
68
69 SDL_SetTextureBlendMode(mTexture, SDL_BLENDMODE_BLEND);
70#if SDL_VERSION_ATLEAST(2, 0, 12)
71 SDL_SetTextureScaleMode(mTexture, SDL_ScaleModeNearest);
72#endif
73
74 int w{};
75 int h{};
76 SDL_QueryTexture(mTexture, nullptr, nullptr, &w, &h);
77 mTransientSurface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGBA8888);
78 if (mTransientSurface == nullptr) {
79 throwException(std::string("Failed to create transient surface: ") + SDL_GetError());
80 }
81 SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA8888, 0);
82 if (mTransientSurface->format != surface->format) {
83 SDL_Surface* converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA8888, 0);
84 if (converted != nullptr) {
85 SDL_BlitScaled(converted, nullptr, mTransientSurface, nullptr);
86 SDL_FreeSurface(converted);
87 }
88 } else {
89 SDL_BlitScaled(surface, nullptr, mTransientSurface, nullptr);
90 }
91
92 if (autoFree) {
93 SDL_FreeSurface(surface);
94 }
95 } else if (autoFree && surface != nullptr) {
96 SDL_FreeSurface(surface);
97 }
98 }
99
100 Image::~Image()
101 {
102 if (mAutoFree) {
103 SDL_FreeSurface(mTransientSurface);
104 mTransientSurface = nullptr;
105 SDL_DestroyTexture(mTexture);
106 mTexture = nullptr;
107 }
108 }
109
110 SDL_Surface* Image::getSurface() const
111 {
112 return mTransientSurface;
113 }
114
115 SDL_Texture* Image::getTexture() const
116 {
117 return mTexture;
118 }
119
120 int Image::getWidth() const
121 {
122 if (mTexture == nullptr) {
123 throwException("Trying to get the width of a non loaded image.");
124 }
125
126 int w{};
127 int h{};
128 SDL_QueryTexture(mTexture, nullptr, nullptr, &w, &h);
129 return w;
130 }
131
133 {
134 if (mTexture == nullptr) {
135 throwException("Trying to get the height of a non loaded image.");
136 }
137
138 int w{};
139 int h{};
140 SDL_QueryTexture(mTexture, nullptr, nullptr, &w, &h);
141 return h;
142 }
143
144 Color Image::getPixel(int x, int y)
145 {
146 if (mTransientSurface == nullptr) {
147 throwException("Trying to get a pixel from a non loaded image.");
148 }
149
150 return SDLgetPixel(mTransientSurface, x, y);
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 SDLputPixel(mTransientSurface, x, y, color);
160 }
161
163 {
164 if (mTexture == nullptr) {
165 throwException("Trying to convert a non loaded image to display format.");
166 }
167 }
168
170 {
171 SDL_FreeSurface(mTransientSurface);
172 mTransientSurface = nullptr;
173 SDL_DestroyTexture(mTexture);
174 mTexture = nullptr;
175 }
176} // namespace fcn::sdl2
Color.
Definition color.hpp:56
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).
Image(SDL_Surface *surface, bool autoFree, SDL_Renderer *renderer=nullptr)
Constructor.
virtual SDL_Texture * getTexture() const
Gets the SDL texture for the image.
bool mAutoFree
Whether the transient surface should be freed on destruction.
int getWidth() const override
Gets the width of the image.
void free() override
Frees an image.
SDL_Texture * mTexture
SDL texture for accelerated rendering (primary storage).
void convertToDisplayFormat() override
Converts the image, if possible, to display format.
int getHeight() const override
Gets the height of the image.
virtual SDL_Surface * getSurface() const
Gets the SDL surface for the image.
Color getPixel(int x, int y) override
Gets the color of a pixel at coordinate (x, y) in the image.
SDL_Renderer * mRenderer
SDL renderer associated with the texture.
Unified header for the SDL backend.
Color SDLgetPixel(SDL_Surface *surface, int x, int y)
Checks a pixels color of an SDL_Surface.
Definition pixel.hpp:27
void SDLputPixel(SDL_Surface *surface, int x, int y, Color const &color)
Puts a pixel on an SDL_Surface.
Definition pixel.hpp:95