spacebox/src/extension.cpp

336 lines
10 KiB
C++

#include "extension.hpp"
glm::vec2 sfw::get_step(glm::vec2 start, glm::vec2 end, float speed)
{
float angle = glm::atan(end.x - start.x, end.y - start.y);
return glm::vec2(speed * glm::sin(angle), speed * glm::cos(angle));
}
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::fill_texture(SDL_Renderer* renderer, SDL_Texture* texture, int r, int g, int b, int a)
{
SDL_SetRenderTarget(renderer, texture);
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::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, Uint32 format)
{
Box box = get_texture_box(base);
SDL_Texture* duplicate = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, box.get_w(), box.get_h());
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;
}
if ((SDL_RenderCopyF(renderer, base, NULL, NULL)) < 0)
{
print_sdl_error("could not render base onto duplicate");
return NULL;
}
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"
#include "hq2x.c"
/*
Base texture must be set to SDL_TEXTUREACCESS_TARGET
Scale2x implementation based on the explanation at 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 << "{" << color.r << ", " << color.g << ", " << color.b << ", " << color.a << "}";
return out;
}