539 lines
18 KiB
C++
539 lines
18 KiB
C++
#include "extension.hpp"
|
|
|
|
glm::vec2 sfw::get_step(const Segment& segment, float speed)
|
|
{
|
|
float angle = glm::atan(segment.end.x - segment.start.x, segment.end.y - segment.start.y);
|
|
return glm::vec2(speed * glm::sin(angle), speed * glm::cos(angle));
|
|
}
|
|
|
|
glm::vec2 sfw::get_step_relative(const Segment& segment, float relative_length_per_step)
|
|
{
|
|
return get_step(
|
|
segment, glm::distance(segment.start, segment.end) * relative_length_per_step);
|
|
}
|
|
|
|
std::vector<Segment> sfw::get_segments(const Segment& base, int count)
|
|
{
|
|
glm::vec2 step = get_step_relative(base, 1.0f / count);
|
|
std::vector<Segment> segments;
|
|
segments.reserve(count);
|
|
glm::vec2 start = base.start, end;
|
|
for (int ii = 0; ii < count; ii++)
|
|
{
|
|
end = start + step;
|
|
segments.emplace_back(start, end);
|
|
start = end;
|
|
}
|
|
return segments;
|
|
}
|
|
|
|
void sfw::set_magnitude(glm::vec2& vector, float magnitude)
|
|
{
|
|
vector = glm::normalize(vector) * magnitude;
|
|
}
|
|
|
|
Box sfw::get_texture_box(SDL_Texture* texture)
|
|
{
|
|
int w, h;
|
|
SDL_QueryTexture(texture, NULL, NULL, &w, &h);
|
|
return Box(glm::vec2(0, 0), glm::vec2(w, h));
|
|
}
|
|
|
|
void sfw::populate_pixel_2d_array(SDL_Renderer* renderer, SDL_Texture* texture, std::vector<std::vector<SDL_Color>>& pixels)
|
|
{
|
|
populate_pixel_2d_array(renderer, texture, pixels, get_texture_box(texture));
|
|
}
|
|
|
|
void sfw::populate_pixel_2d_array(
|
|
SDL_Renderer* renderer, SDL_Texture* texture, std::vector<std::vector<SDL_Color>>& pixels, const Box& region)
|
|
{
|
|
int access;
|
|
if (SDL_QueryTexture(texture, NULL, &access, NULL, NULL) < 0)
|
|
{
|
|
print_sdl_error("Could not query texture for access flag");
|
|
}
|
|
else
|
|
{
|
|
if (access != SDL_TEXTUREACCESS_TARGET)
|
|
{
|
|
texture = duplicate_texture(renderer, texture);
|
|
}
|
|
if (SDL_SetRenderTarget(renderer, texture) < 0)
|
|
{
|
|
print_sdl_error("Could not set render target");
|
|
}
|
|
else
|
|
{
|
|
Uint32 format = SDL_PIXELFORMAT_RGBA32;
|
|
int bytes_per_pixel = SDL_BYTESPERPIXEL(format);
|
|
int bytes_per_row = bytes_per_pixel * region.get_w();
|
|
int bytes_total = bytes_per_row * region.get_h();
|
|
Uint8* source = new Uint8[bytes_total];
|
|
SDL_Rect int_rect = region.get_int_rect();
|
|
if (SDL_RenderReadPixels(renderer, &int_rect, format, source, bytes_per_row) < 0)
|
|
{
|
|
print_sdl_error("Could not read pixels after setting remapped texture as target");
|
|
}
|
|
else
|
|
{
|
|
pixels.reserve(region.get_w());
|
|
for (int x = 0; x < region.get_w(); x++)
|
|
{
|
|
std::vector<SDL_Color> column;
|
|
pixels.push_back(column);
|
|
pixels[x].reserve(region.get_h());
|
|
}
|
|
for (int y = 0, ii = 0; y < region.get_h(); y++)
|
|
{
|
|
for (int x = 0; x < region.get_w(); x++)
|
|
{
|
|
pixels[x][y] = {source[ii++], source[ii++], source[ii++], source[ii++]};
|
|
}
|
|
}
|
|
}
|
|
delete[] source;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<SDL_Texture*> sfw::get_halo_frames(
|
|
Node& node, float radius, int segment_count, const std::vector<SDL_Color>& colors, float min_radius, bool fade)
|
|
{
|
|
std::vector<SDL_Texture*> frames;
|
|
frames.reserve(segment_count);
|
|
SDL_Renderer* renderer = node.get_renderer();
|
|
SDL_Texture* frame;
|
|
float alpha = 255, alpha_step = 255.0f / segment_count, segment_radius;
|
|
int color_count = colors.size();
|
|
SDL_Color color;
|
|
for (int color_offset = 0; color_offset < color_count; color_offset++)
|
|
{
|
|
if (fade)
|
|
{
|
|
alpha = alpha_step;
|
|
}
|
|
frame = sfw::get_filled_texture(renderer, {2 * radius, 2 * radius}, {255, 255, 255, 0});
|
|
SDL_SetTextureBlendMode(frame, SDL_BLENDMODE_BLEND);
|
|
SDL_SetRenderTarget(renderer, frame);
|
|
for (int segment_ii = 0; segment_ii < segment_count; segment_ii++)
|
|
{
|
|
color = colors[(color_offset + segment_ii) % color_count];
|
|
color.a = std::round(alpha);
|
|
segment_radius = min_radius + (segment_count - 1.0f - segment_ii) / (segment_count - 1.0f) * (radius - min_radius);
|
|
aaFilledEllipseRGBA(
|
|
renderer, radius, radius, segment_radius, segment_radius, color.r, color.g, color.b, color.a);
|
|
if (fade)
|
|
{
|
|
alpha += alpha_step;
|
|
}
|
|
}
|
|
frames.push_back(frame);
|
|
}
|
|
return frames;
|
|
}
|
|
|
|
std::vector<SDL_Texture*> sfw::get_portal_frames(
|
|
SDL_Renderer* renderer, glm::vec2 size, float hue_start, float hue_end, int dy, int count)
|
|
{
|
|
std::vector<SDL_Texture*> frames;
|
|
frames.reserve(count);
|
|
float y_margin = 10;
|
|
float max_y = size.y - y_margin;
|
|
std::vector<float> hues = range_step(hue_start, hue_end, count);
|
|
std::cout << hues << std::endl;
|
|
SDL_Texture* frame;
|
|
Color color;
|
|
for (int frame_ii = 0; frame_ii < count; frame_ii++)
|
|
{
|
|
frame = sfw::get_filled_texture(renderer, size, {255, 255, 255, 0});
|
|
SDL_SetRenderTarget(renderer, frame);
|
|
SDL_SetTextureBlendMode(frame, SDL_BLENDMODE_BLEND);
|
|
for (int ellipse_ii = 0, y = max_y; y > y_margin - 3; ellipse_ii++, y -= dy)
|
|
{
|
|
color.a = y / max_y * 255.0f;
|
|
color.set_hsv(hues[mod(ellipse_ii - frame_ii, count)]);
|
|
aaFilledEllipseRGBA(renderer, size.x / 2, y, size.x / 2, y_margin - 3, color.r, color.g, color.b, color.a);
|
|
}
|
|
frames.push_back(frame);
|
|
}
|
|
return frames;
|
|
}
|
|
|
|
void sfw::fill_texture(SDL_Renderer* renderer, SDL_Texture* texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
|
|
{
|
|
SDL_SetRenderTarget(renderer, texture);
|
|
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
|
|
SDL_SetRenderDrawColor(renderer, r, g, b, a);
|
|
SDL_RenderFillRect(renderer, NULL);
|
|
}
|
|
|
|
void sfw::fill_texture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Texture* tile)
|
|
{
|
|
SDL_SetRenderTarget(renderer, texture);
|
|
Box texture_box = get_texture_box(texture), tile_box = get_texture_box(tile);
|
|
SDL_FRect draw_rect;
|
|
for (int x = 0; x < texture_box.get_w(); x += tile_box.get_w())
|
|
{
|
|
for (int y = 0; y < texture_box.get_h(); y += tile_box.get_h())
|
|
{
|
|
draw_rect = {(float) x, (float) y, tile_box.get_w(), tile_box.get_h()};
|
|
SDL_RenderCopyF(renderer, tile, NULL, &draw_rect);
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_Texture* sfw::get_filled_texture(SDL_Renderer* renderer, glm::vec2 size, const SDL_Color& color, Uint32 format)
|
|
{
|
|
SDL_Texture* texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y);
|
|
sfw::fill_texture(renderer, texture, color.r, color.g, color.b, color.a);
|
|
return texture;
|
|
}
|
|
|
|
SDL_Texture* sfw::get_filled_texture(SDL_Renderer* renderer, glm::vec2 size, SDL_Texture* tile, Uint32 format)
|
|
{
|
|
SDL_Texture* texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y);
|
|
sfw::fill_texture(renderer, texture, tile);
|
|
return texture;
|
|
}
|
|
|
|
SDL_Texture* sfw::get_hue_shifted_texture(SDL_Renderer* renderer, SDL_Texture* base, float offset)
|
|
{
|
|
SDL_Texture* hue_shifted_texture = sfw::duplicate_texture(renderer, base);
|
|
Uint32 pixel_format;
|
|
int w, h;
|
|
SDL_QueryTexture(hue_shifted_texture, &pixel_format, NULL, &w, &h);
|
|
SDL_PixelFormat* pixel_format_struct = SDL_AllocFormat(pixel_format);
|
|
SDL_SetRenderTarget(renderer, hue_shifted_texture);
|
|
int bytes_per_pixel = SDL_BYTESPERPIXEL(pixel_format);
|
|
int bytes_per_row = bytes_per_pixel * w;
|
|
int bytes_total = bytes_per_row * h;
|
|
int length = bytes_total / 4 + (bytes_total % 4 ? 1 : 0);
|
|
Uint32* pixels = new Uint32[length];
|
|
if (SDL_RenderReadPixels(renderer, NULL, pixel_format, pixels, bytes_per_row) < 0)
|
|
{
|
|
print_sdl_error("Could not read pixels");
|
|
}
|
|
else
|
|
{
|
|
Color rgba;
|
|
for (int ii = 0; ii < length; ii++)
|
|
{
|
|
SDL_GetRGBA(pixels[ii], const_cast<const SDL_PixelFormat*>(pixel_format_struct),
|
|
&rgba.r, &rgba.g, &rgba.b, &rgba.a);
|
|
rgba.shift_hue(offset);
|
|
pixels[ii] = SDL_MapRGBA(const_cast<const SDL_PixelFormat*>(pixel_format_struct), rgba.r, rgba.g, rgba.b, rgba.a);
|
|
}
|
|
if (SDL_UpdateTexture(hue_shifted_texture, NULL, pixels, bytes_per_row) < 0)
|
|
{
|
|
print_sdl_error("Could not apply hue shifted pixels update to texture");
|
|
}
|
|
}
|
|
delete[] pixels;
|
|
SDL_FreeFormat(pixel_format_struct);
|
|
return hue_shifted_texture;
|
|
}
|
|
|
|
SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, Uint32 format)
|
|
{
|
|
Box box = get_texture_box(base);
|
|
return duplicate_texture(renderer, base, box.get_size(), format);
|
|
}
|
|
|
|
SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, const glm::vec2& size, Uint32 format)
|
|
{
|
|
SDL_BlendMode original_blend_mode;
|
|
SDL_GetTextureBlendMode(base, &original_blend_mode);
|
|
SDL_Texture* duplicate = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y);
|
|
if (duplicate == NULL)
|
|
{
|
|
print_sdl_error("could not create texture from base");
|
|
return NULL;
|
|
}
|
|
if ((SDL_SetRenderTarget(renderer, duplicate)) < 0)
|
|
{
|
|
print_sdl_error("could not set render target to duplicate");
|
|
return NULL;
|
|
}
|
|
SDL_SetTextureBlendMode(base, SDL_BLENDMODE_NONE);
|
|
SDL_SetTextureBlendMode(duplicate, SDL_BLENDMODE_BLEND);
|
|
if ((SDL_RenderCopyF(renderer, base, NULL, NULL)) < 0)
|
|
{
|
|
print_sdl_error("could not render base onto duplicate");
|
|
return NULL;
|
|
}
|
|
SDL_SetTextureBlendMode(base, original_blend_mode);
|
|
SDL_SetTextureBlendMode(duplicate, original_blend_mode);
|
|
return duplicate;
|
|
}
|
|
|
|
SDL_Texture* sfw::get_remapped_texture(
|
|
SDL_Renderer* renderer, SDL_Texture* base, const std::map<SDL_Color, SDL_Color>& map, Uint32 format)
|
|
{
|
|
SDL_Texture* remapped = duplicate_texture(renderer, base, format);
|
|
if (remapped == NULL)
|
|
{
|
|
print_sdl_error("could not duplicate base texture");
|
|
return NULL;
|
|
}
|
|
if ((SDL_SetRenderTarget(renderer, remapped)) < 0)
|
|
{
|
|
print_sdl_error("could not set render target to remapped texture");
|
|
return NULL;
|
|
}
|
|
Box box = get_texture_box(remapped);
|
|
int bytes_per_pixel = SDL_BYTESPERPIXEL(format);
|
|
int bytes_total = bytes_per_pixel * box.get_w() * box.get_h();
|
|
unsigned char* pixels = new unsigned char[bytes_total];
|
|
if ((SDL_RenderReadPixels(renderer, NULL, format, pixels, bytes_total / box.get_h())) < 0)
|
|
{
|
|
print_sdl_error("could not read pixels after setting remapped texture as target");
|
|
return NULL;
|
|
}
|
|
SDL_Color color;
|
|
for (int ii = 0; ii < bytes_total; ii += bytes_per_pixel)
|
|
{
|
|
color = {pixels[ii], pixels[ii + 1], pixels[ii + 2], 255};
|
|
if (bytes_per_pixel == 4)
|
|
{
|
|
color.a = pixels[ii + 3];
|
|
}
|
|
for (auto& pair : map)
|
|
{
|
|
if (color.r == pair.first.r && color.g == pair.first.g && color.b == pair.first.b &&
|
|
(bytes_per_pixel < 4 || color.a == pair.first.a))
|
|
{
|
|
pixels[ii] = pair.second.r;
|
|
pixels[ii + 1] = pair.second.g;
|
|
pixels[ii + 2] = pair.second.b;
|
|
if (bytes_per_pixel == 4)
|
|
{
|
|
pixels[ii + 3] = pair.second.a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (SDL_UpdateTexture(remapped, NULL, pixels, bytes_total / box.get_h()) < 0)
|
|
{
|
|
print_sdl_error("could not update remapped texture");
|
|
}
|
|
delete[] pixels;
|
|
return remapped;
|
|
}
|
|
|
|
SDL_Texture* sfw::get_remapped_texture(
|
|
SDL_Renderer* renderer, const std::string& path, const std::map<SDL_Color, SDL_Color>& map, Uint32 format)
|
|
{
|
|
SDL_Texture* base = IMG_LoadTexture(renderer, path.c_str());
|
|
if (base == NULL)
|
|
{
|
|
print_sdl_error("error loading file");
|
|
return NULL;
|
|
}
|
|
SDL_Texture* remapped = get_remapped_texture(renderer, base, map, format);
|
|
if (remapped == NULL)
|
|
{
|
|
print_error("could not remap texture");
|
|
return NULL;
|
|
}
|
|
SDL_DestroyTexture(base);
|
|
return remapped;
|
|
}
|
|
|
|
#include "superxbr.cpp"
|
|
|
|
/*
|
|
- Base texture must be set to SDL_TEXTUREACCESS_TARGET
|
|
- Scale2x implementation based on http://www.scale2x.it/algorithm.html
|
|
*/
|
|
SDL_Texture* sfw::get_pixel_scaled_texture(SDL_Renderer* renderer, SDL_Texture* base, int count, int version)
|
|
{
|
|
if ((SDL_SetRenderTarget(renderer, base)) < 0)
|
|
{
|
|
print_sdl_error("could not set render target to remapped texture");
|
|
return NULL;
|
|
}
|
|
glm::ivec2 size = get_texture_box(base).get_size();
|
|
Uint32 format = SDL_PIXELFORMAT_RGBA32;
|
|
int bytes_per_pixel, bytes_per_row, bytes_total;
|
|
Uint32 *src, *dst, *src_begin, *dst_begin;
|
|
for (int ii = 0; ii < count; ii++, size *= 2)
|
|
{
|
|
bytes_per_pixel = SDL_BYTESPERPIXEL(format);
|
|
bytes_per_row = bytes_per_pixel * size.x;
|
|
bytes_total = bytes_per_row * size.y;
|
|
if (ii == 0)
|
|
{
|
|
src = new Uint32[size.x * size.y];
|
|
src_begin = src;
|
|
if ((SDL_RenderReadPixels(renderer, NULL, format, src, bytes_per_row)) < 0)
|
|
{
|
|
print_sdl_error("could not read pixels after setting remapped texture as target");
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
src = dst_begin;
|
|
src_begin = src;
|
|
}
|
|
dst = new Uint32[size.x * size.y * 4];
|
|
dst_begin = dst;
|
|
if (version == scaler::scale2x)
|
|
{
|
|
Uint32 A, B, C, D, E, F, G, H, I;
|
|
for (int y = 0; y < size.y; y++)
|
|
{
|
|
for (int x = 0; x < size.x; x++)
|
|
{
|
|
E = *src;
|
|
B = y == 0 ? E : *(src - size.x);
|
|
D = x == 0 ? E : *(src - 1);
|
|
F = x == size.x - 1 ? E : *(src + 1);
|
|
H = y == size.y - 1 ? E : *(src + size.x);
|
|
if (y != 0 && x != 0 && y != size.y - 1 && x != size.x - 1)
|
|
{
|
|
A = *(src - size.x - 1);
|
|
C = *(src - size.x + 1);
|
|
G = *(src + size.x - 1);
|
|
I = *(src + size.x + 1);
|
|
}
|
|
if (x == 0)
|
|
{
|
|
A = B;
|
|
G = H;
|
|
}
|
|
if (y == 0)
|
|
{
|
|
A = D;
|
|
C = F;
|
|
}
|
|
if (x == size.x - 1)
|
|
{
|
|
C = B;
|
|
I = H;
|
|
}
|
|
if (y == size.y - 1)
|
|
{
|
|
G = D;
|
|
I = F;
|
|
}
|
|
if (B != H && D != F)
|
|
{
|
|
*dst = D == B ? D : E;
|
|
*(dst + 1) = B == F ? F : E;
|
|
*(dst + 2 * size.x) = D == H ? D : E;
|
|
*(dst + 2 * size.x + 1) = H == F ? F : E;
|
|
}
|
|
else
|
|
{
|
|
*dst = E;
|
|
*(dst + 1) = E;
|
|
*(dst + 2 * size.x) = E;
|
|
*(dst + 2 * size.x + 1) = E;
|
|
}
|
|
src++;
|
|
dst += 2;
|
|
}
|
|
dst += 2 * size.x;
|
|
}
|
|
}
|
|
else if (version == scaler::xbr)
|
|
{
|
|
scaleSuperXBRT<2>(src, dst, size.x, size.y);
|
|
}
|
|
delete[] src_begin;
|
|
}
|
|
SDL_Texture* scaled = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y);
|
|
if (scaled == NULL)
|
|
{
|
|
print_sdl_error("could not create scaled texture");
|
|
}
|
|
if (SDL_UpdateTexture(scaled, NULL, dst_begin, bytes_per_row * 2) < 0)
|
|
{
|
|
print_sdl_error("could not copy pixels to scaled texture");
|
|
}
|
|
delete[] dst_begin;
|
|
return scaled;
|
|
}
|
|
|
|
std::vector<fs::path> sfw::glob(fs::path query)
|
|
{
|
|
fs::path basename = query.parent_path();
|
|
if (basename == "")
|
|
{
|
|
basename = ".";
|
|
}
|
|
std::regex expression(query.string());
|
|
std::vector<fs::path> files;
|
|
for (auto& entry: fs::directory_iterator(basename))
|
|
{
|
|
if (std::regex_match(entry.path().string(), expression))
|
|
{
|
|
files.push_back(entry.path());
|
|
}
|
|
}
|
|
std::sort(files.begin(), files.end());
|
|
return files;
|
|
}
|
|
|
|
fs::path sfw::get_next_file_name(
|
|
fs::path directory, int zfill, std::string prefix, std::string extension)
|
|
{
|
|
std::stringstream file_pattern;
|
|
file_pattern << prefix << "([0-9]+)" << extension;
|
|
fs::path query = directory / file_pattern.str();
|
|
std::vector<fs::path> files = sfw::glob(query);
|
|
int index = 1;
|
|
if (files.size())
|
|
{
|
|
const std::string last = files.back().string();
|
|
std::smatch matches;
|
|
std::regex_match(last, matches, std::regex(query.string()));
|
|
index = std::stoi(matches[1]) + 1;
|
|
}
|
|
std::stringstream filename;
|
|
fs::path path;
|
|
do
|
|
{
|
|
filename << prefix << sfw::pad(index++, zfill) << extension;
|
|
path = directory / filename.str();
|
|
filename.str("");
|
|
filename.clear();
|
|
}
|
|
while (fs::exists(path));
|
|
return path;
|
|
}
|
|
|
|
void sfw::print_error(const std::string& message)
|
|
{
|
|
std::cerr << message << std::endl;
|
|
}
|
|
|
|
void sfw::print_sdl_error(const std::string& message)
|
|
{
|
|
std::cerr << message << " " << SDL_GetError() << std::endl;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& out, const glm::vec2& vector)
|
|
{
|
|
out << "{" << vector.x << ", " << vector.y << "}";
|
|
return out;
|
|
}
|
|
|
|
bool operator<(const SDL_Color& color_1, const SDL_Color& color_2)
|
|
{
|
|
return color_1.r < color_2.r || color_1.g < color_2.g || color_1.b < color_2.b || color_1.a < color_2.a;
|
|
}
|
|
|
|
bool operator==(const SDL_Color& color_1, const SDL_Color& color_2)
|
|
{
|
|
return color_1.r == color_2.r && color_1.g == color_2.g && color_1.b == color_2.b && color_1.a == color_2.a;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& out, const SDL_Color& color)
|
|
{
|
|
out << "{" << static_cast<int>(color.r) << ", " << static_cast<int>(color.g) << ", " <<
|
|
static_cast<int>(color.b) << ", " << static_cast<int>(color.a) << "}";
|
|
return out;
|
|
}
|