MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
defender_assets.cpp
Go to the documentation of this file.
1#include "defender_assets.hpp"
2
4
6#include "mxvk/mxvk_png.hpp"
7
8#include <SDL3/SDL.h>
9
10#include <algorithm>
11#include <cstddef>
12#include <cstdint>
13#include <string>
14#include <vector>
15
16namespace defender {
17
18 SpriteAlphaBounds calculate_alpha_bounds(SDL_Surface *surface, std::uint8_t threshold) {
19 if (surface == nullptr || surface->w <= 0 || surface->h <= 0) {
20 return {};
21 }
22
23 const SDL_PixelFormatDetails *format_details = SDL_GetPixelFormatDetails(surface->format);
24 if (format_details == nullptr || !SDL_LockSurface(surface)) {
25 return {};
26 }
27
28 int min_x = surface->w;
29 int min_y = surface->h;
30 int max_x = -1;
31 int max_y = -1;
32 const auto *bytes = static_cast<const std::uint8_t *>(surface->pixels);
33
34 for (int y = 0; y < surface->h; ++y) {
35 const auto *row = reinterpret_cast<const std::uint32_t *>(bytes + static_cast<std::size_t>(y) * static_cast<std::size_t>(surface->pitch));
36 for (int x = 0; x < surface->w; ++x) {
37 std::uint8_t r = 0;
38 std::uint8_t g = 0;
39 std::uint8_t b = 0;
40 std::uint8_t a = 0;
41 SDL_GetRGBA(row[x], format_details, nullptr, &r, &g, &b, &a);
42 if (a <= threshold) {
43 continue;
44 }
45 min_x = std::min(min_x, x);
46 min_y = std::min(min_y, y);
47 max_x = std::max(max_x, x);
48 max_y = std::max(max_y, y);
49 }
50 }
51
52 SDL_UnlockSurface(surface);
53
54 if (max_x < min_x || max_y < min_y) {
55 return {};
56 }
57
58 const float width = static_cast<float>(surface->w);
59 const float height = static_cast<float>(surface->h);
60 return {
61 static_cast<float>(min_x) / width - 0.5f,
62 static_cast<float>(max_x + 1) / width - 0.5f,
63 0.5f - static_cast<float>(max_y + 1) / height,
64 0.5f - static_cast<float>(min_y) / height,
65 };
66 }
67
68 SDL_Surface *load_light_background_png(const std::string &path) {
69 SDL_Surface *loaded_surface = mxvk::LoadPNG(path.c_str());
70 if (loaded_surface == nullptr) {
71 throw mxvk::Exception("Failed to load PNG: " + path);
72 }
73
74 SDL_Surface *surface = SDL_ConvertSurface(loaded_surface, SDL_PIXELFORMAT_RGBA32);
75 SDL_DestroySurface(loaded_surface);
76 if (surface == nullptr) {
77 throw mxvk::Exception("Failed to convert PNG to RGBA: " + path);
78 }
79
80 const SDL_PixelFormatDetails *format_details = SDL_GetPixelFormatDetails(surface->format);
81 if (format_details == nullptr) {
82 SDL_DestroySurface(surface);
83 throw mxvk::Exception("Failed to query pixel format details for: " + path);
84 }
85
86 if (!SDL_LockSurface(surface)) {
87 SDL_DestroySurface(surface);
88 throw mxvk::Exception("Failed to lock PNG surface: " + path);
89 }
90
91 auto *pixels = static_cast<std::uint32_t *>(surface->pixels);
92 const int width = surface->w;
93 const int height = surface->h;
94 const int pixel_count = width * height;
95 std::vector<std::uint8_t> background(static_cast<std::size_t>(pixel_count), 0);
96 std::vector<int> stack;
97 stack.reserve(static_cast<std::size_t>(width + height) * 2);
98
99 const auto is_light_checker_pixel = [&](const int index) {
100 std::uint8_t r = 0;
101 std::uint8_t g = 0;
102 std::uint8_t b = 0;
103 std::uint8_t a = 0;
104 SDL_GetRGBA(pixels[index], format_details, nullptr, &r, &g, &b, &a);
105 const int min_channel = std::min({static_cast<int>(r), static_cast<int>(g), static_cast<int>(b)});
106 const int max_channel = std::max({static_cast<int>(r), static_cast<int>(g), static_cast<int>(b)});
107 return a != 0 && min_channel >= 215 && (max_channel - min_channel) <= 28;
108 };
109
110 const auto push_background = [&](const int index) {
111 if (index < 0 || index >= pixel_count || background[static_cast<std::size_t>(index)] != 0 || !is_light_checker_pixel(index)) {
112 return;
113 }
114 background[static_cast<std::size_t>(index)] = 1;
115 stack.push_back(index);
116 };
117
118 for (int x = 0; x < width; ++x) {
119 push_background(x);
120 push_background((height - 1) * width + x);
121 }
122 for (int y = 0; y < height; ++y) {
123 push_background(y * width);
124 push_background(y * width + width - 1);
125 }
126
127 while (!stack.empty()) {
128 const int index = stack.back();
129 stack.pop_back();
130 const int x = index % width;
131 const int y = index / width;
132 if (x > 0) {
133 push_background(index - 1);
134 }
135 if (x + 1 < width) {
136 push_background(index + 1);
137 }
138 if (y > 0) {
139 push_background(index - width);
140 }
141 if (y + 1 < height) {
142 push_background(index + width);
143 }
144 }
145
146 for (int i = 0; i < pixel_count; ++i) {
147 if (background[static_cast<std::size_t>(i)] == 0) {
148 continue;
149 }
150 std::uint8_t r = 0;
151 std::uint8_t g = 0;
152 std::uint8_t b = 0;
153 std::uint8_t a = 0;
154 SDL_GetRGBA(pixels[i], format_details, nullptr, &r, &g, &b, &a);
155 pixels[i] = SDL_MapRGBA(format_details, nullptr, r, g, b, 0);
156 }
157
158 SDL_UnlockSurface(surface);
159 return surface;
160 }
161
162} // namespace defender
PNG image loading and saving utilities via SDL3.
SpriteAlphaBounds calculate_alpha_bounds(SDL_Surface *surface, std::uint8_t threshold)
SDL_Surface * load_light_background_png(const std::string &path)
SDL_Surface * LoadPNG(const char *file)
Load a PNG file into an SDL_Surface.
Definition mxvk_png.cpp:103