- sprite additions: multiple locations, auto wrapping, scale mod

- remap colors on a texture
- two pixel scaling algorithms: custom scale2x based on http://www.scale2x.it/algorithm.html and superxbr
This commit is contained in:
Frank DeMarco 2020-07-25 17:55:27 -04:00
parent 3d439e56e0
commit bc2a4a39ad
10 changed files with 881 additions and 91 deletions

375
lib/superxbr.cpp Normal file
View File

@ -0,0 +1,375 @@
//// *** Super-xBR code begins here - MIT LICENSE *** ///
/*
******* Super XBR Scaler *******
Copyright (c) 2016 Hyllian - sergiogdb@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <cstdint>
typedef uint32_t u32;
#define R(_col) ((_col>> 0)&0xFF)
#define G(_col) ((_col>> 8)&0xFF)
#define B(_col) ((_col>>16)&0xFF)
#define A(_col) ((_col>>24)&0xFF)
#define wgt1 0.129633f
#define wgt2 0.175068f
#define w1 (-wgt1)
#define w2 (wgt1+0.5f)
#define w3 (-wgt2)
#define w4 (wgt2+0.5f)
float df(float A, float B)
{
return abs(A - B);
}
float min4(float a, float b, float c, float d)
{
return std::min(std::min(a,b),std::min(c, d));
}
float max4(float a, float b, float c, float d)
{
return std::max(std::max(a, b), std::max(c, d));
}
template<class T>
T clamp(T x, T floor, T ceil)
{
return std::max(std::min(x, ceil), floor);
}
/*
P1
|P0|B |C |P1| C F4 |a0|b1|c2|d3|
|D |E |F |F4| B F I4 |b0|c1|d2|e3| |e1|i1|i2|e2|
|G |H |I |I4| P0 E A I P3 |c0|d1|e2|f3| |e3|i3|i4|e4|
|P2|H5|I5|P3| D H I5 |d0|e1|f2|g3|
G H5
P2
sx, sy
-1 -1 | -2 0 (x+y) (x-y) -3 1 (x+y-1) (x-y+1)
-1 0 | -1 -1 -2 0
-1 1 | 0 -2 -1 -1
-1 2 | 1 -3 0 -2
0 -1 | -1 1 (x+y) (x-y) ... ... ...
0 0 | 0 0
0 1 | 1 -1
0 2 | 2 -2
1 -1 | 0 2 ...
1 0 | 1 1
1 1 | 2 0
1 2 | 3 -1
2 -1 | 1 3 ...
2 0 | 2 2
2 1 | 3 1
2 2 | 4 0
*/
float diagonal_edge(float mat[][4], float *wp) {
float dw1 = wp[0]*(df(mat[0][2], mat[1][1]) + df(mat[1][1], mat[2][0]) + df(mat[1][3], mat[2][2]) + df(mat[2][2], mat[3][1])) +\
wp[1]*(df(mat[0][3], mat[1][2]) + df(mat[2][1], mat[3][0])) + \
wp[2]*(df(mat[0][3], mat[2][1]) + df(mat[1][2], mat[3][0])) +\
wp[3]*df(mat[1][2], mat[2][1]) +\
wp[4]*(df(mat[0][2], mat[2][0]) + df(mat[1][3], mat[3][1])) +\
wp[5]*(df(mat[0][1], mat[1][0]) + df(mat[2][3], mat[3][2]));
float dw2 = wp[0]*(df(mat[0][1], mat[1][2]) + df(mat[1][2], mat[2][3]) + df(mat[1][0], mat[2][1]) + df(mat[2][1], mat[3][2])) +\
wp[1]*(df(mat[0][0], mat[1][1]) + df(mat[2][2], mat[3][3])) +\
wp[2]*(df(mat[0][0], mat[2][2]) + df(mat[1][1], mat[3][3])) +\
wp[3]*df(mat[1][1], mat[2][2]) +\
wp[4]*(df(mat[1][0], mat[3][2]) + df(mat[0][1], mat[2][3])) +\
wp[5]*(df(mat[0][2], mat[1][3]) + df(mat[2][0], mat[3][1]));
return (dw1 - dw2);
}
// Not used yet...
float cross_edge(float mat[][4], float *wp) {
float hvw1 = wp[3] * (df(mat[1][1], mat[2][1]) + df(mat[1][2], mat[2][2])) + \
wp[0] * (df(mat[0][1], mat[1][1]) + df(mat[2][1], mat[3][1]) + df(mat[0][2], mat[1][2]) + df(mat[2][2], mat[3][2])) + \
wp[2] * (df(mat[0][1], mat[2][1]) + df(mat[1][1], mat[3][1]) + df(mat[0][2], mat[2][2]) + df(mat[1][2], mat[3][2]));
float hvw2 = wp[3] * (df(mat[1][1], mat[1][2]) + df(mat[2][1], mat[2][2])) + \
wp[0] * (df(mat[1][0], mat[1][1]) + df(mat[2][0], mat[2][1]) + df(mat[1][2], mat[1][3]) + df(mat[2][2], mat[2][3])) + \
wp[2] * (df(mat[1][0], mat[1][2]) + df(mat[1][1], mat[1][3]) + df(mat[2][0], mat[2][2]) + df(mat[2][1], mat[2][3]));
return (hvw1 - hvw2);
}
///////////////////////// Super-xBR scaling
// perform super-xbr (fast shader version) scaling by factor f=2 only.
template<int f>
void scaleSuperXBRT(u32* data, u32* out, int w, int h) {
int outw = w*f, outh = h*f;
float wp[6] = { 2.0f, 1.0f, -1.0f, 4.0f, -1.0f, 1.0f };
// First Pass
for (int y = 0; y < outh; ++y) {
for (int x = 0; x < outw; ++x) {
float r[4][4], g[4][4], b[4][4], a[4][4], Y[4][4];
int cx = x / f, cy = y / f; // central pixels on original images
// sample supporting pixels in original image
for (int sx = -1; sx <= 2; ++sx) {
for (int sy = -1; sy <= 2; ++sy) {
// clamp pixel locations
int csy = clamp(sy + cy, 0, h - 1);
int csx = clamp(sx + cx, 0, w - 1);
// sample & add weighted components
u32 sample = data[csy*w + csx];
r[sx + 1][sy + 1] = (float)R(sample);
g[sx + 1][sy + 1] = (float)G(sample);
b[sx + 1][sy + 1] = (float)B(sample);
a[sx + 1][sy + 1] = (float)A(sample);
Y[sx + 1][sy + 1] = (float)(0.2126*r[sx + 1][sy + 1] + 0.7152*g[sx + 1][sy + 1] + 0.0722*b[sx + 1][sy + 1]);
}
}
float min_r_sample = min4(r[1][1], r[2][1], r[1][2], r[2][2]);
float min_g_sample = min4(g[1][1], g[2][1], g[1][2], g[2][2]);
float min_b_sample = min4(b[1][1], b[2][1], b[1][2], b[2][2]);
float min_a_sample = min4(a[1][1], a[2][1], a[1][2], a[2][2]);
float max_r_sample = max4(r[1][1], r[2][1], r[1][2], r[2][2]);
float max_g_sample = max4(g[1][1], g[2][1], g[1][2], g[2][2]);
float max_b_sample = max4(b[1][1], b[2][1], b[1][2], b[2][2]);
float max_a_sample = max4(a[1][1], a[2][1], a[1][2], a[2][2]);
float d_edge = diagonal_edge(Y, &wp[0]);
float r1, g1, b1, a1, r2, g2, b2, a2, rf, gf, bf, af;
r1 = (float)w1*(r[0][3] + r[3][0]) + (float)w2*(r[1][2] + r[2][1]);
g1 = (float)w1*(g[0][3] + g[3][0]) + (float)w2*(g[1][2] + g[2][1]);
b1 = (float)w1*(b[0][3] + b[3][0]) + (float)w2*(b[1][2] + b[2][1]);
a1 = (float)w1*(a[0][3] + a[3][0]) + (float)w2*(a[1][2] + a[2][1]);
r2 = (float)w1*(r[0][0] + r[3][3]) + (float)w2*(r[1][1] + r[2][2]);
g2 = (float)w1*(g[0][0] + g[3][3]) + (float)w2*(g[1][1] + g[2][2]);
b2 = (float)w1*(b[0][0] + b[3][3]) + (float)w2*(b[1][1] + b[2][2]);
a2 = (float)w1*(a[0][0] + a[3][3]) + (float)w2*(a[1][1] + a[2][2]);
// generate and write result
if (d_edge <= 0.0f) { rf = r1; gf = g1; bf = b1; af = a1; }
else { rf = r2; gf = g2; bf = b2; af = a2; }
// anti-ringing, clamp.
rf = clamp(rf, min_r_sample, max_r_sample);
gf = clamp(gf, min_g_sample, max_g_sample);
bf = clamp(bf, min_b_sample, max_b_sample);
af = clamp(af, min_a_sample, max_a_sample);
int ri = clamp(static_cast<int>(ceilf(rf)), 0, 255);
int gi = clamp(static_cast<int>(ceilf(gf)), 0, 255);
int bi = clamp(static_cast<int>(ceilf(bf)), 0, 255);
int ai = clamp(static_cast<int>(ceilf(af)), 0, 255);
out[y*outw + x] = out[y*outw + x + 1] = out[(y + 1)*outw + x] = data[cy*w + cx];
out[(y+1)*outw + x+1] = (ai << 24) | (bi << 16) | (gi << 8) | ri;
++x;
}
++y;
}
// Second Pass
wp[0] = 2.0f;
wp[1] = 0.0f;
wp[2] = 0.0f;
wp[3] = 0.0f;
wp[4] = 0.0f;
wp[5] = 0.0f;
for (int y = 0; y < outh; ++y) {
for (int x = 0; x < outw; ++x) {
float r[4][4], g[4][4], b[4][4], a[4][4], Y[4][4];
// sample supporting pixels in original image
for (int sx = -1; sx <= 2; ++sx) {
for (int sy = -1; sy <= 2; ++sy) {
// clamp pixel locations
int csy = clamp(sx - sy + y, 0, f*h - 1);
int csx = clamp(sx + sy + x, 0, f*w - 1);
// sample & add weighted components
u32 sample = out[csy*outw + csx];
r[sx + 1][sy + 1] = (float)R(sample);
g[sx + 1][sy + 1] = (float)G(sample);
b[sx + 1][sy + 1] = (float)B(sample);
a[sx + 1][sy + 1] = (float)A(sample);
Y[sx + 1][sy + 1] = (float)(0.2126*r[sx + 1][sy + 1] + 0.7152*g[sx + 1][sy + 1] + 0.0722*b[sx + 1][sy + 1]);
}
}
float min_r_sample = min4(r[1][1], r[2][1], r[1][2], r[2][2]);
float min_g_sample = min4(g[1][1], g[2][1], g[1][2], g[2][2]);
float min_b_sample = min4(b[1][1], b[2][1], b[1][2], b[2][2]);
float min_a_sample = min4(a[1][1], a[2][1], a[1][2], a[2][2]);
float max_r_sample = max4(r[1][1], r[2][1], r[1][2], r[2][2]);
float max_g_sample = max4(g[1][1], g[2][1], g[1][2], g[2][2]);
float max_b_sample = max4(b[1][1], b[2][1], b[1][2], b[2][2]);
float max_a_sample = max4(a[1][1], a[2][1], a[1][2], a[2][2]);
float d_edge = diagonal_edge(Y, &wp[0]);
float r1, g1, b1, a1, r2, g2, b2, a2, rf, gf, bf, af;
r1 = (float)w3*(r[0][3] + r[3][0]) + (float)w4*(r[1][2] + r[2][1]);
g1 = (float)w3*(g[0][3] + g[3][0]) + (float)w4*(g[1][2] + g[2][1]);
b1 = (float)w3*(b[0][3] + b[3][0]) + (float)w4*(b[1][2] + b[2][1]);
a1 = (float)w3*(a[0][3] + a[3][0]) + (float)w4*(a[1][2] + a[2][1]);
r2 = (float)w3*(r[0][0] + r[3][3]) + (float)w4*(r[1][1] + r[2][2]);
g2 = (float)w3*(g[0][0] + g[3][3]) + (float)w4*(g[1][1] + g[2][2]);
b2 = (float)w3*(b[0][0] + b[3][3]) + (float)w4*(b[1][1] + b[2][2]);
a2 = (float)w3*(a[0][0] + a[3][3]) + (float)w4*(a[1][1] + a[2][2]);
// generate and write result
if (d_edge <= 0.0f) { rf = r1; gf = g1; bf = b1; af = a1; }
else { rf = r2; gf = g2; bf = b2; af = a2; }
// anti-ringing, clamp.
rf = clamp(rf, min_r_sample, max_r_sample);
gf = clamp(gf, min_g_sample, max_g_sample);
bf = clamp(bf, min_b_sample, max_b_sample);
af = clamp(af, min_a_sample, max_a_sample);
int ri = clamp(static_cast<int>(ceilf(rf)), 0, 255);
int gi = clamp(static_cast<int>(ceilf(gf)), 0, 255);
int bi = clamp(static_cast<int>(ceilf(bf)), 0, 255);
int ai = clamp(static_cast<int>(ceilf(af)), 0, 255);
out[y*outw + x + 1] = (ai << 24) | (bi << 16) | (gi << 8) | ri;
for (int sx = -1; sx <= 2; ++sx) {
for (int sy = -1; sy <= 2; ++sy) {
// clamp pixel locations
int csy = clamp(sx - sy + 1 + y, 0, f*h - 1);
int csx = clamp(sx + sy - 1 + x, 0, f*w - 1);
// sample & add weighted components
u32 sample = out[csy*outw + csx];
r[sx + 1][sy + 1] = (float)R(sample);
g[sx + 1][sy + 1] = (float)G(sample);
b[sx + 1][sy + 1] = (float)B(sample);
a[sx + 1][sy + 1] = (float)A(sample);
Y[sx + 1][sy + 1] = (float)(0.2126*r[sx + 1][sy + 1] + 0.7152*g[sx + 1][sy + 1] + 0.0722*b[sx + 1][sy + 1]);
}
}
d_edge = diagonal_edge(Y, &wp[0]);
r1 = (float)w3*(r[0][3] + r[3][0]) + (float)w4*(r[1][2] + r[2][1]);
g1 = (float)w3*(g[0][3] + g[3][0]) + (float)w4*(g[1][2] + g[2][1]);
b1 = (float)w3*(b[0][3] + b[3][0]) + (float)w4*(b[1][2] + b[2][1]);
a1 = (float)w3*(a[0][3] + a[3][0]) + (float)w4*(a[1][2] + a[2][1]);
r2 = (float)w3*(r[0][0] + r[3][3]) + (float)w4*(r[1][1] + r[2][2]);
g2 = (float)w3*(g[0][0] + g[3][3]) + (float)w4*(g[1][1] + g[2][2]);
b2 = (float)w3*(b[0][0] + b[3][3]) + (float)w4*(b[1][1] + b[2][2]);
a2 = (float)w3*(a[0][0] + a[3][3]) + (float)w4*(a[1][1] + a[2][2]);
// generate and write result
if (d_edge <= 0.0f) { rf = r1; gf = g1; bf = b1; af = a1; }
else { rf = r2; gf = g2; bf = b2; af = a2; }
// anti-ringing, clamp.
rf = clamp(rf, min_r_sample, max_r_sample);
gf = clamp(gf, min_g_sample, max_g_sample);
bf = clamp(bf, min_b_sample, max_b_sample);
af = clamp(af, min_a_sample, max_a_sample);
ri = clamp(static_cast<int>(ceilf(rf)), 0, 255);
gi = clamp(static_cast<int>(ceilf(gf)), 0, 255);
bi = clamp(static_cast<int>(ceilf(bf)), 0, 255);
ai = clamp(static_cast<int>(ceilf(af)), 0, 255);
out[(y+1)*outw + x] = (ai << 24) | (bi << 16) | (gi << 8) | ri;
++x;
}
++y;
}
// Third Pass
wp[0] = 2.0f;
wp[1] = 1.0f;
wp[2] = -1.0f;
wp[3] = 4.0f;
wp[4] = -1.0f;
wp[5] = 1.0f;
for (int y = outh - 1; y >= 0; --y) {
for (int x = outw - 1; x >= 0; --x) {
float r[4][4], g[4][4], b[4][4], a[4][4], Y[4][4];
for (int sx = -2; sx <= 1; ++sx) {
for (int sy = -2; sy <= 1; ++sy) {
// clamp pixel locations
int csy = clamp(sy + y, 0, f*h - 1);
int csx = clamp(sx + x, 0, f*w - 1);
// sample & add weighted components
u32 sample = out[csy*outw + csx];
r[sx + 2][sy + 2] = (float)R(sample);
g[sx + 2][sy + 2] = (float)G(sample);
b[sx + 2][sy + 2] = (float)B(sample);
a[sx + 2][sy + 2] = (float)A(sample);
Y[sx + 2][sy + 2] = (float)(0.2126*r[sx + 2][sy + 2] + 0.7152*g[sx + 2][sy + 2] + 0.0722*b[sx + 2][sy + 2]);
}
}
float min_r_sample = min4(r[1][1], r[2][1], r[1][2], r[2][2]);
float min_g_sample = min4(g[1][1], g[2][1], g[1][2], g[2][2]);
float min_b_sample = min4(b[1][1], b[2][1], b[1][2], b[2][2]);
float min_a_sample = min4(a[1][1], a[2][1], a[1][2], a[2][2]);
float max_r_sample = max4(r[1][1], r[2][1], r[1][2], r[2][2]);
float max_g_sample = max4(g[1][1], g[2][1], g[1][2], g[2][2]);
float max_b_sample = max4(b[1][1], b[2][1], b[1][2], b[2][2]);
float max_a_sample = max4(a[1][1], a[2][1], a[1][2], a[2][2]);
float d_edge = diagonal_edge(Y, &wp[0]);
float r1, g1, b1, a1, r2, g2, b2, a2, rf, gf, bf, af;
r1 = (float)w1*(r[0][3] + r[3][0]) + (float)w2*(r[1][2] + r[2][1]);
g1 = (float)w1*(g[0][3] + g[3][0]) + (float)w2*(g[1][2] + g[2][1]);
b1 = (float)w1*(b[0][3] + b[3][0]) + (float)w2*(b[1][2] + b[2][1]);
a1 = (float)w1*(a[0][3] + a[3][0]) + (float)w2*(a[1][2] + a[2][1]);
r2 = (float)w1*(r[0][0] + r[3][3]) + (float)w2*(r[1][1] + r[2][2]);
g2 = (float)w1*(g[0][0] + g[3][3]) + (float)w2*(g[1][1] + g[2][2]);
b2 = (float)w1*(b[0][0] + b[3][3]) + (float)w2*(b[1][1] + b[2][2]);
a2 = (float)w1*(a[0][0] + a[3][3]) + (float)w2*(a[1][1] + a[2][2]);
// generate and write result
if (d_edge <= 0.0f) { rf = r1; gf = g1; bf = b1; af = a1; }
else { rf = r2; gf = g2; bf = b2; af = a2; }
// anti-ringing, clamp.
rf = clamp(rf, min_r_sample, max_r_sample);
gf = clamp(gf, min_g_sample, max_g_sample);
bf = clamp(bf, min_b_sample, max_b_sample);
af = clamp(af, min_a_sample, max_a_sample);
int ri = clamp(static_cast<int>(ceilf(rf)), 0, 255);
int gi = clamp(static_cast<int>(ceilf(gf)), 0, 255);
int bi = clamp(static_cast<int>(ceilf(bf)), 0, 255);
int ai = clamp(static_cast<int>(ceilf(af)), 0, 255);
out[y*outw + x] = (ai << 24) | (bi << 16) | (gi << 8) | ri;
}
}
}
//// *** Super-xBR code ends here - MIT LICENSE *** ///
// void scaleSuperXBR(int factor, u32* data, u32* out, int w, int h) {
/* Needs implementation.*/
// switch (factor) {
// case 2: scaleSuperXBRT<2>(data, out, w, h); break;
// default: ERROR_LOG(VIDEO, "Super-xBR upsampling only implemented for factor 2");
// }
// }

View File

@ -1,10 +1,6 @@
#include "Box.hpp"
Box::Box(glm::vec2 nw, glm::vec2 size)
{
set_nw(nw);
set_size(size);
}
Box::Box(const glm::vec2& nw, const glm::vec2& size) : rect({nw.x, nw.y, size.x, size.y}) {}
float Box::get_x() const
{
@ -134,8 +130,7 @@ glm::vec2 Box::get_center() const
void Box::set_nw(const glm::vec2& nw)
{
set_x(nw.x);
set_y(nw.y);
move(nw - get_nw());
}
void Box::set_north(const glm::vec2& n)
@ -173,12 +168,23 @@ SDL_FRect* Box::get_rect()
return &rect;
}
void Box::zero()
SDL_Rect Box::get_int_rect()
{
return {static_cast<int>(rect.x), static_cast<int>(rect.y), static_cast<int>(rect.w), static_cast<int>(rect.h)};
}
void Box::clear()
{
set_nw(glm::vec2(0, 0));
set_size(glm::vec2(0, 0));
}
void Box::scale(float amount)
{
set_w(get_w() * amount);
set_h(get_h() * amount);
}
void Box::move(const glm::vec2& delta)
{
set_x(get_x() + delta.x);

View File

@ -11,9 +11,9 @@
struct Box
{
SDL_FRect rect = {0, 0, 0, 0};
SDL_FRect rect;
Box(glm::vec2 = {0, 0}, glm::vec2 = {0, 0});
Box(const glm::vec2& = {0, 0}, const glm::vec2& = {0, 0});
float get_x() const;
float get_y() const;
float get_w() const;
@ -47,7 +47,9 @@ struct Box
void set_west(const glm::vec2&);
void set_center(const glm::vec2&);
SDL_FRect* get_rect();
void zero();
SDL_Rect get_int_rect();
void clear();
void scale(float);
void move(const glm::vec2&);
std::string get_class_name() { return "Box"; }
std::ostream& to_string (std::ostream&) const;

View File

@ -38,14 +38,16 @@ void Display::get_screen_pixels(unsigned char* pixels, int w, int h, int x, int
}
else
{
Uint32 format;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
format = SDL_PIXELFORMAT_ARGB8888;
#else
format = SDL_PIXELFORMAT_ABGR8888;
#endif
// Uint32 format;
// #if SDL_BYTEORDER == SDL_BIG_ENDIAN
// format = SDL_PIXELFORMAT_ARGB8888;
// #else
// format = SDL_PIXELFORMAT_ABGR8888;
// #endif
SDL_SetRenderTarget(get_root()->renderer, NULL);
SDL_RenderPresent(get_root()->renderer);
SDL_RenderReadPixels(
get_root()->renderer, NULL, format, pixels, bpp / 8 * w);
get_root()->renderer, NULL, SDL_PIXELFORMAT_RGBA32, pixels, bpp / 8 * w);
}
}

View File

@ -40,7 +40,7 @@ void FramerateIndicator::refresh()
SDL_Texture* texture = SDL_CreateTextureFromSurface(get_root()->get_renderer(), surface);
add_frame(texture);
SDL_FreeSurface(surface);
box.set_ne(get_display().get_window_box().get_ne());
set_ne(get_display().get_window_box().get_ne());
}
}
@ -136,14 +136,14 @@ Game::~Game()
get_delegate().unsubscribe(this);
}
void Game::print_error(std::string message)
void Game::print_error(const std::string& message)
{
std::cerr << message << std::endl;
sfw::print_error(message);
}
void Game::print_sdl_error(std::string message)
void Game::print_sdl_error(const std::string& message)
{
std::cerr << message << " " << SDL_GetError() << std::endl;
sfw::print_sdl_error(message);
}
void Game::print_gl_attributes()
@ -177,7 +177,8 @@ void Game::load_sdl_context()
SDL_GL_DeleteContext(glcontext);
glcontext = NULL;
}
if ((renderer = SDL_CreateRenderer(window, -1, 0)) == NULL)
if ((renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE)) == NULL)
{
print_sdl_error("Could not create renderer");
flag_to_end();
@ -463,10 +464,10 @@ void Game::run()
emscripten_set_main_loop_arg(&loop, this, -1, true);
#else
SDL_Log("using standard main loop");
while (not done)
while (!done)
{
frame(SDL_GetTicks());
SDL_Delay(8);
SDL_Delay(0);
}
#endif
}
@ -476,6 +477,7 @@ void Game::frame(float ticks)
// std::cout << "frame_length: " << frame_length << ", ticks: " << ticks << ", last_frame_timestamp: " << last_frame_timestamp <<
// ", frame_time_overflow: " << frame_time_overflow;
if (ticks - last_frame_timestamp + frame_time_overflow >= frame_length)
// if (ticks - last_frame_timestamp >= frame_length)
{
last_frame_length = ticks - last_frame_timestamp;
if (frame_length_history.size() == 5000)
@ -486,20 +488,26 @@ void Game::frame(float ticks)
frame_time_overflow = last_frame_length + frame_time_overflow - frame_length;
last_frame_timestamp = ticks;
// std::cout << ", last_frame_length: " << last_frame_length << " [rendering frame]";
recorder.update();
delegate.dispatch();
get_root()->input.unsuppress_animation.update();
update();
framerate_indicator.update();
if (!is_gl_context)
if (last_frame_length < 1000)
{
SDL_RenderPresent(renderer);
}
if (frame_time_overflow > frame_length)
{
// SDL_Log("%i frame(s) dropped", ((int) (frame_time_overflow / frame_length)));
frame_time_overflow = 0;
if (!is_gl_context)
{
recorder.update();
}
delegate.dispatch();
get_root()->input.unsuppress_animation.update();
update();
framerate_indicator.update();
if (!is_gl_context)
{
SDL_RenderPresent(renderer);
}
}
// if (frame_time_overflow > frame_length)
// {
// // SDL_Log("%i frame(s) dropped", ((int) (frame_time_overflow / frame_length)));
// frame_time_overflow = 0;
// }
frame_count_this_second++;
if (ticks - last_frame_count_timestamp >= 1000)
{

View File

@ -67,8 +67,8 @@ struct Game : Node
Game();
~Game();
void print_error(std::string);
void print_sdl_error(std::string);
void print_error(const std::string&);
void print_sdl_error(const std::string&);
void print_gl_attributes();
void print_frame_length_history();
void load_sdl_context();
@ -92,6 +92,7 @@ struct Game : Node
template<typename T>
float weight(T amount)
{
// std::cout << amount << " - " << last_frame_length << std::endl;
return (last_frame_length / (1000.0 / 60)) * amount;
}

View File

@ -109,9 +109,41 @@ Frameset& Sprite::get_current_frameset()
return framesets[current_frameset_name];
}
Box& Sprite::get_box(int index)
{
return boxes[index];
}
void Sprite::add_box(glm::vec2 position, bool absolute)
{
if (!absolute)
{
position += get_nw();
}
boxes.emplace_back(glm::vec2(position.x, position.y), glm::vec2(get_w(), get_h()));
}
void Sprite::update_size()
{
box.set_size(get_current_frameset().get_size());
for (Box& box : boxes)
{
box.set_size(get_current_frameset().get_size());
}
}
void Sprite::set_scale(float s)
{
scale = s;
for (auto& member : framesets)
{
member.second.set_size(member.second.get_size() * s);
}
update_size();
}
float Sprite::get_scale() const
{
return scale;
}
bool Sprite::is_loaded() const
@ -126,7 +158,10 @@ void Sprite::unload()
SDL_DestroyTexture(frames.back());
frames.pop_back();
}
box.zero();
for (Box& box : boxes)
{
box.clear();
}
}
void Sprite::advance_frame()
@ -160,69 +195,156 @@ void Sprite::set_step(glm::vec2 s)
step.y = s.y;
}
float Sprite::get_w() const
void Sprite::set_alpha_mod(Uint8 mod)
{
return box.get_w();
alpha_mod = mod;
}
float Sprite::get_h() const
Uint8 Sprite::get_alpha_mod() const
{
return box.get_h();
return alpha_mod;
}
glm::vec2 Sprite::get_size() const
float Sprite::get_w()
{
return box.get_size();
return get_box().get_w();
}
float Sprite::get_top() const
float Sprite::get_h()
{
return box.get_top();
return get_box().get_h();
}
float Sprite::get_right() const
glm::vec2 Sprite::get_size()
{
return box.get_right();
return get_box().get_size();
}
float Sprite::get_bottom() const
float Sprite::get_top(int index)
{
return box.get_bottom();
return get_box(index).get_top();
}
float Sprite::get_left() const
float Sprite::get_right(int index)
{
return box.get_left();
return get_box(index).get_right();
}
glm::vec2 Sprite::get_nw() const
float Sprite::get_bottom(int index)
{
return box.get_nw();
return get_box(index).get_bottom();
}
glm::vec2 Sprite::get_north() const
float Sprite::get_left(int index)
{
return box.get_north();
return get_box(index).get_left();
}
glm::vec2 Sprite::get_west() const
glm::vec2 Sprite::get_nw(int index)
{
return box.get_west();
return get_box(index).get_nw();
}
glm::vec2 Sprite::get_north(int index)
{
return get_box(index).get_north();
}
glm::vec2 Sprite::get_ne(int index)
{
return get_box(index).get_ne();
}
glm::vec2 Sprite::get_east(int index)
{
return get_box(index).get_east();
}
glm::vec2 Sprite::get_west(int index)
{
return get_box(index).get_west();
}
glm::vec2 Sprite::get_center(int index)
{
return get_box(index).get_center();
}
void Sprite::set_nw(const glm::vec2& nw)
{
box.set_nw(nw);
move(nw - get_nw(), false);
}
void Sprite::move(glm::vec2 delta, bool weighted)
void Sprite::set_ne(const glm::vec2& ne)
{
move(ne - get_ne(), false);
}
void Sprite::set_center(const glm::vec2& center)
{
move(center - get_center(), false);
}
void Sprite::add_wrap(bool x, bool y)
{
add_wrap(x, y, get_display().get_window_box());
}
void Sprite::add_wrap(bool x, bool y, Box frame)
{
wrap = {x, y};
int original_box_count = boxes.size();
for (int ii = 0; ii < original_box_count; ii++)
{
if (x)
{
add_box({frame.get_w(), 0});
}
if (y)
{
add_box({0, frame.get_h()});
}
if (x && y)
{
add_box({frame.get_w(), frame.get_h()});
}
}
wrap_frame = frame;
}
glm::vec2 Sprite::move(glm::vec2 delta, bool weighted)
{
Game *game = get_root();
if (weighted)
{
delta = game->weight(delta);
delta = get_root()->weight(delta);
}
box.move(delta);
for (Box& box : boxes)
{
box.move(delta);
}
if (wrap.x)
{
if (get_right() > wrap_frame.get_right())
{
move({-wrap_frame.get_w(), 0}, false);
}
else if (get_right() < wrap_frame.get_left())
{
move({wrap_frame.get_w(), 0}, false);
}
}
if (wrap.y)
{
if (get_bottom() > wrap_frame.get_bottom())
{
move({0, -wrap_frame.get_h()}, false);
}
else if (get_bottom() < wrap_frame.get_top())
{
move({0, wrap_frame.get_h()}, false);
}
}
return delta;
}
void Sprite::update()
@ -239,7 +361,19 @@ void Sprite::update()
SDL_Renderer* renderer = get_root()->renderer;
SDL_SetTextureAlphaMod(texture, alpha_mod);
SDL_SetRenderTarget(renderer, NULL);
SDL_RenderCopyF(renderer, texture, NULL, box.get_rect());
if (wrap.x || wrap.y)
{
SDL_Rect wrap_frame_rect = wrap_frame.get_int_rect();
SDL_RenderSetClipRect(renderer, &wrap_frame_rect);
}
for (Box& box : boxes)
{
SDL_RenderCopyF(renderer, texture, NULL, box.get_rect());
}
if (wrap.x || wrap.y)
{
SDL_RenderSetClipRect(renderer, NULL);
}
}
}
}
@ -293,8 +427,8 @@ glm::vec2 Frameset::measure() const
if (index < sprite->frames.size())
{
SDL_QueryTexture(sprite->frames[index], NULL, NULL, &w, &h);
s.x = std::max(static_cast<float>(w), s.x);
s.y = std::max(static_cast<float>(h), s.y);
s.x = std::max(static_cast<float>(w * sprite->scale), s.x);
s.y = std::max(static_cast<float>(h * sprite->scale), s.y);
}
}
return s;

View File

@ -23,14 +23,17 @@ struct Sprite : Node
std::vector<SDL_Texture*> frames;
std::vector<fs::path> frame_paths;
Box box;
std::vector<Box> boxes = {{{0, 0}, {0, 0}}};
Animation frame_animation = Animation(&Sprite::advance_frame, this);
Animation blink_animation = Animation(&Sprite::toggle_hidden, this, 500);
bool hidden = false;
glm::vec2 step = {0, 0};
float scale = 1;
Uint8 alpha_mod = 255;
std::map<std::string, Frameset> framesets;
std::string current_frameset_name;
glm::bvec2 wrap = {false, false};
Box wrap_frame;
Sprite();
Sprite(Node*);
@ -44,7 +47,11 @@ struct Sprite : Node
Frameset& add_frameset(std::string);
void set_frameset(std::string);
Frameset& get_current_frameset();
Box& get_box(int = 0);
void add_box(glm::vec2, bool = false);
void update_size();
void set_scale(float);
float get_scale() const;
bool is_loaded() const;
void unload();
void advance_frame();
@ -53,18 +60,27 @@ struct Sprite : Node
void toggle_hidden();
bool is_hidden() const;
void set_step(glm::vec2);
float get_w() const;
float get_h() const;
glm::vec2 get_size() const;
float get_top() const;
float get_right() const;
float get_bottom() const;
float get_left() const;
glm::vec2 get_nw() const;
glm::vec2 get_north() const;
glm::vec2 get_west() const;
void set_alpha_mod(Uint8);
Uint8 get_alpha_mod() const;
float get_w();
float get_h();
glm::vec2 get_size();
float get_top(int = 0);
float get_right(int = 0);
float get_bottom(int = 0);
float get_left(int = 0);
glm::vec2 get_nw(int = 0);
glm::vec2 get_north(int = 0);
glm::vec2 get_ne(int = 0);
glm::vec2 get_east(int = 0);
glm::vec2 get_west(int = 0);
glm::vec2 get_center(int = 0);
void set_nw(const glm::vec2&);
void move(glm::vec2, bool = true);
void set_ne(const glm::vec2&);
void set_center(const glm::vec2&);
void add_wrap(bool, bool);
void add_wrap(bool, bool, Box);
glm::vec2 move(glm::vec2, bool = true);
void update();
std::string get_class_name() { return "Sprite"; }
~Sprite() { unload(); }

View File

@ -40,16 +40,220 @@ void sfw::fill_texture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Texture
}
}
SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* texture)
SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, Uint32 format)
{
Box box = get_texture_box(texture);
SDL_Texture* duplicate = SDL_CreateTexture(
renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, box.get_w(), box.get_h());
SDL_SetRenderTarget(renderer, duplicate);
SDL_RenderCopyF(renderer, texture, NULL, NULL);
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();
@ -98,8 +302,34 @@ fs::path sfw::get_next_file_name(
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;
}

View File

@ -8,28 +8,41 @@
#include <algorithm>
#include <iomanip>
#include <stdexcept>
#include <map>
#include "SDL.h"
#include "SDL_image.h"
#define GLM_ENABLE_EXPERIMENTAL
#include "glm/trigonometric.hpp"
#include "glm/vec2.hpp"
#include "glm/gtx/vector_angle.hpp"
#include "SDL.h"
#include "Box.hpp"
#include "Color.hpp"
#include "filesystem.hpp"
namespace sfw
{
enum scaler {scale2x, xbr};
glm::vec2 get_step(glm::vec2, glm::vec2, float);
void set_magnitude(glm::vec2&, float);
Box get_texture_box(SDL_Texture*);
void fill_texture(SDL_Renderer*, SDL_Texture*, int, int, int, int = 0xff);
void fill_texture(SDL_Renderer*, SDL_Texture*, SDL_Texture*);
SDL_Texture* duplicate_texture(SDL_Renderer*, SDL_Texture*);
SDL_Texture* duplicate_texture(SDL_Renderer*, SDL_Texture*, Uint32 = SDL_PIXELFORMAT_RGBA32);
SDL_Texture* get_remapped_texture(
SDL_Renderer*, SDL_Texture*, const std::map<SDL_Color, SDL_Color>&, Uint32 = SDL_PIXELFORMAT_RGBA32);
SDL_Texture* get_remapped_texture(
SDL_Renderer*, const std::string&, const std::map<SDL_Color, SDL_Color>&,
Uint32 = SDL_PIXELFORMAT_RGBA32);
SDL_Texture* get_pixel_scaled_texture(SDL_Renderer*, SDL_Texture*, int = 1, int = scaler::scale2x);
std::vector<fs::path> glob(fs::path);
fs::path get_next_file_name(
fs::path, int = 0, std::string = "", std::string = "");
void print_error(const std::string&);
void print_sdl_error(const std::string&);
template<typename T1, typename T2>
bool is_in_container(T1& container, T2& member)
@ -81,5 +94,8 @@ namespace sfw
}
std::ostream& operator<<(std::ostream&, const glm::vec2&);
bool operator<(const SDL_Color& color_1, const SDL_Color& color_2);
bool operator==(const SDL_Color& color_1, const SDL_Color& color_2);
std::ostream& operator<<(std::ostream&, const SDL_Color&);
#endif