diff --git a/lib/superxbr.cpp b/lib/superxbr.cpp new file mode 100644 index 0000000..f031619 --- /dev/null +++ b/lib/superxbr.cpp @@ -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 + +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 +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 +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(ceilf(rf)), 0, 255); + int gi = clamp(static_cast(ceilf(gf)), 0, 255); + int bi = clamp(static_cast(ceilf(bf)), 0, 255); + int ai = clamp(static_cast(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(ceilf(rf)), 0, 255); + int gi = clamp(static_cast(ceilf(gf)), 0, 255); + int bi = clamp(static_cast(ceilf(bf)), 0, 255); + int ai = clamp(static_cast(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(ceilf(rf)), 0, 255); + gi = clamp(static_cast(ceilf(gf)), 0, 255); + bi = clamp(static_cast(ceilf(bf)), 0, 255); + ai = clamp(static_cast(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(ceilf(rf)), 0, 255); + int gi = clamp(static_cast(ceilf(gf)), 0, 255); + int bi = clamp(static_cast(ceilf(bf)), 0, 255); + int ai = clamp(static_cast(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"); +// } + +// } diff --git a/src/Box.cpp b/src/Box.cpp index 1a7323b..debb30d 100644 --- a/src/Box.cpp +++ b/src/Box.cpp @@ -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 ▭ } -void Box::zero() +SDL_Rect Box::get_int_rect() +{ + return {static_cast(rect.x), static_cast(rect.y), static_cast(rect.w), static_cast(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); diff --git a/src/Box.hpp b/src/Box.hpp index 86ed636..f628716 100644 --- a/src/Box.hpp +++ b/src/Box.hpp @@ -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; diff --git a/src/Display.cpp b/src/Display.cpp index 3d0aba6..96fca21 100644 --- a/src/Display.cpp +++ b/src/Display.cpp @@ -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); } } diff --git a/src/Game.cpp b/src/Game.cpp index 30fbbef..2e8fd4d 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -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) { diff --git a/src/Game.hpp b/src/Game.hpp index abbc1a3..f02eb9f 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -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 float weight(T amount) { + // std::cout << amount << " - " << last_frame_length << std::endl; return (last_frame_length / (1000.0 / 60)) * amount; } diff --git a/src/Sprite.cpp b/src/Sprite.cpp index bed6af5..dfeb727 100644 --- a/src/Sprite.cpp +++ b/src/Sprite.cpp @@ -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(w), s.x); - s.y = std::max(static_cast(h), s.y); + s.x = std::max(static_cast(w * sprite->scale), s.x); + s.y = std::max(static_cast(h * sprite->scale), s.y); } } return s; diff --git a/src/Sprite.hpp b/src/Sprite.hpp index 2258f0a..646b8f1 100644 --- a/src/Sprite.hpp +++ b/src/Sprite.hpp @@ -23,14 +23,17 @@ struct Sprite : Node std::vector frames; std::vector frame_paths; - Box box; + std::vector 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 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(); } diff --git a/src/extension.cpp b/src/extension.cpp index 46984dd..18b0ce3 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -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& 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& 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 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; +} diff --git a/src/extension.hpp b/src/extension.hpp index 7ed52b7..d8d0b8a 100644 --- a/src/extension.hpp +++ b/src/extension.hpp @@ -8,28 +8,41 @@ #include #include #include +#include + +#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&, Uint32 = SDL_PIXELFORMAT_RGBA32); + SDL_Texture* get_remapped_texture( + SDL_Renderer*, const std::string&, const std::map&, + Uint32 = SDL_PIXELFORMAT_RGBA32); + SDL_Texture* get_pixel_scaled_texture(SDL_Renderer*, SDL_Texture*, int = 1, int = scaler::scale2x); std::vector 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 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