upscalers

This commit is contained in:
Squishy Bloob 2024-03-30 19:08:38 +00:00
parent e83ed1172c
commit 702aa679b9
15 changed files with 328 additions and 23 deletions

View File

@ -19,6 +19,7 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\draw\upscale.cpp" />
<ClCompile Include="src\util.cpp" />
<ClCompile Include="src\ini.cpp" />
<ClCompile Include="src\draw\draw_text.cpp" />
@ -42,6 +43,7 @@
<ClInclude Include="include\util.h" />
<ClInclude Include="include\vec2.h" />
<ClInclude Include="include\resource_manager.h" />
<ClInclude Include="include\math_util.h" />
<ClInclude Include="src\stb_image.h" />
</ItemGroup>
<ItemGroup>

View File

@ -42,6 +42,9 @@
<ClCompile Include="src\util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\draw\upscale.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\game.h">
@ -83,6 +86,9 @@
<ClInclude Include="include\util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\math_util.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="fontconvert.bat">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ShowAllFiles>false</ShowAllFiles>
<ShowAllFiles>true</ShowAllFiles>
</PropertyGroup>
</Project>

View File

@ -4,6 +4,7 @@
#include "game.h"
#include "image.h"
#include "ini.h"
#include "math_util.h"
#include "rect.h"
#include "resource.h"
#include "resource_manager.h"
@ -12,8 +13,6 @@
#include "util.h"
#include "vec2.h"
const double PI = 3.141592653589793238462643383279502884197169399375105820974944592307816406286;
const double TAU = PI * 2;
/*

View File

@ -10,6 +10,12 @@
using color = uint32_t;
struct ini;
enum class upscaler {
none, scale2x, scale3x, lcd3x, rgb3x, bilinear3x, hatch3x
};
int upscaler_scale(upscaler upscaler);
struct image : resource {
image(resource_manager& rm, ini_category const* ini, std::string const& path, std::vector<uint8_t> const& data);
image(image const& other);
@ -23,6 +29,7 @@ struct image : resource {
color* raw_pointer() const;
color get(vec2i pos) const;
color get_safe(vec2i pos) const;
void clear(color color);
@ -30,11 +37,19 @@ struct image : resource {
void draw(image const* image, vec2i pos, recti src_rect);
void draw(image const* image, vec2i pos, color color);
void draw(image const* image, vec2i pos, color color, recti src_rect);
void draw_upscaled(image const* image);
//void draw_upscaled(image const* image);
void draw_rot(image const* image, vec2i pos, double rot);
void draw_rot(image const* image, vec2i pos, recti src_rect, double rot);
void draw_upscaled(image const* image, upscaler upscaler);
void draw_upscaled(image const* image, std::vector<upscaler> upscalers);
image const* upscale_eagle_2x() const;
void draw(std::string const& str, vec2i pos, font const* font, color color);
void draw(std::string const& str, vec2i pos, font const* font, color color1, color color2);
private:
void draw_upscaled(image const* image, vec2i size, upscaler upscaler);
bool _alpha;
bool _borrowed_pointer; //to know if to free or not
color* _data; //not a vector so can wrap sdl surfaces

View File

@ -0,0 +1,5 @@
#pragma once
const double PI = 3.141592653589793238462643383279502884197169399375105820974944592307816406286;
const double TAU = (PI * 2);

View File

@ -1,4 +1,5 @@
#include "image.h"
#include "math_util.h"
void image::draw(image const* image, vec2i pos) {
draw(image, pos, image->bounds());
@ -22,16 +23,12 @@ void image::draw(image const* image, vec2i pos, recti src_rect) {
}
}
} else {
//#pragma omp parallel for
for(int y = start.y; y <= end.y; ++y) {
std::memcpy(_data + (y * _bounds.size.x), image->_data - start.x + src_off.x + (y-start.y + src_off.y) * image->_bounds.size.x, (end.x - start.x + 1)*4);
/* for(int x = start.x; x <= end.x; ++x) {
auto color = image->_data[
x - start.x + src_off.x + (y - start.y + src_off.y) * image->_bounds.size.x
];
_data[x + y * _bounds.size.x] = color;
}
*/
std::memcpy(
_data + (y * _bounds.size.x),
image->_data - start.x + src_off.x + (y-start.y + src_off.y) * image->_bounds.size.x,
(end.x - start.x + 1)*4
);
}
}
}
@ -59,7 +56,7 @@ void image::draw(image const* image, vec2i pos, color color, recti src_rect) {
}
}
/*
void image::draw_upscaled(image const* image) {
if(image->bounds() == bounds()) {
std::memcpy(_data, image->_data, bounds().size.size() * 4);
@ -101,3 +98,64 @@ void image::draw_upscaled(image const* image) {
}
}
}
*/
void image::draw_rot(image const* image, vec2i pos, double rot) {
draw_rot(image, pos, image->bounds(), rot);
}
void image::draw_rot(image const* image, vec2i pos, recti src_rect, double rot) {
src_rect = recti::intersection(src_rect, image->bounds());
//auto crop = recti::intersection(bounds(), recti(pos, image->bounds().size));
//crop = recti::intersection(crop, recti(crop.pos, src_rect.size));
//todo: crop to screen...
//todo: fmod the rot?
//rot = rot/2;
//if(rot >= 0.5)
// rot = 0.5 - rot;
rot /= 2;
if(rot >= 0.25)
rot = 0.25 - (rot - 0.25);
rot -= 0.125;
auto crop = recti::intersection(recti(pos, src_rect.size), recti(pos, image->bounds().size));
auto src_off = crop.pos - pos + src_rect.pos;
auto start = crop.pos;
auto end = crop.pos2();
auto xshear = -std::tan(rot * TAU / 2.0) *crop.size.x;
auto yshear = std::sin(rot * TAU) * crop.size.y;
//auto xshear = (std::cos(rot * TAU) - 1) / sin(rot * TAU);
std::vector<int> xshears;
for(int y = start.y; y <= end.y; ++y) {
auto yf = static_cast<double>(y - crop.pos.y) / crop.size.y * 2 - 0.5;
xshears.push_back(static_cast<int>(xshear * yf));
}
for(int y = 0; y <= end.y - start.y; ++y) {
auto yf = static_cast<double>(y) / crop.size.y * 2 - 1;
for(int x = 0; x <= end.x - start.x; ++x) {
double xf, yf;
int nx, ny;
nx = x;
ny = y;
yf = (static_cast<double>(ny) / crop.size.y * 1 - 0.5);
nx = nx + yf * xshear;
//int nxt = x;
xf = (static_cast<double>(nx) / crop.size.x * 1 - 0.5);
ny = ny + xf * yshear;
yf = (static_cast<double>(ny) / crop.size.y * 1 - 0.5);
nx = nx + yf * xshear;
auto color = image->_data[
x + src_off.x + (y + src_off.y) * image->_bounds.size.x
];
if(color & 0xff000000)
_data[nx + start.x + (ny+start.y) * _bounds.size.x] = color;
}
}
}

View File

@ -0,0 +1,199 @@
#include "image.h"
#define UPSCALE_1X \
for(int y = 0; y < s.y; ++y) {\
for(int x = 0; x < s.x; ++x) {\
color in, out;\
in = img->get(vec2i(x, y));
#define UPSCALE_1X_END \
_data[x + y * size().x] = out;\
}\
}\
break;
#define UPSCALE_2X \
for(int y = 0; y < s.y; ++y) {\
for(int x = 0; x < s.x; ++x) {\
color in_t, in_b, in_l, in_r, in_m;\
color in_tl, in_tr, in_bl, in_br;\
color out_tl, out_tr, out_bl, out_br;\
in_t = img->get_safe(vec2i(x, y - 1));\
in_b = img->get_safe(vec2i(x, y + 1));\
in_l = img->get_safe(vec2i(x - 1, y));\
in_r = img->get_safe(vec2i(x + 1, y));\
in_tl = img->get_safe(vec2i(x - 1, y - 1));\
in_tr = img->get_safe(vec2i(x + 1, y - 1));\
in_bl = img->get_safe(vec2i(x - 1, y + 1));\
in_br = img->get_safe(vec2i(x + 1, y + 1));\
in_m = img->get(vec2i(x, y));
#define UPSCALE_2X_END \
_data[(x * 2 + 0) + (y * 2 + 0) * size().x] = out_tl;\
_data[(x * 2 + 1) + (y * 2 + 0) * size().x] = out_tr;\
_data[(x * 2 + 0) + (y * 2 + 1) * size().x] = out_bl;\
_data[(x * 2 + 1) + (y * 2 + 1) * size().x] = out_br;\
}\
}\
break;
#define UPSCALE_3X \
for(int y = 0; y < s.y; ++y) {\
for(int x = 0; x < s.x; ++x) {\
color in_t, in_b, in_l, in_r, in_m;\
color in_tl, in_tr, in_bl, in_br;\
color out_t, out_b, out_l, out_r, out_m;\
color out_tl, out_tr, out_bl, out_br;\
in_t = img->get_safe(vec2i(x, y - 1));\
in_b = img->get_safe(vec2i(x, y + 1));\
in_l = img->get_safe(vec2i(x - 1, y));\
in_r = img->get_safe(vec2i(x + 1, y));\
in_tl = img->get_safe(vec2i(x - 1, y - 1));\
in_tr = img->get_safe(vec2i(x + 1, y - 1));\
in_bl = img->get_safe(vec2i(x - 1, y + 1));\
in_br = img->get_safe(vec2i(x + 1, y + 1));\
in_m = img->get(vec2i(x, y));
#define UPSCALE_3X_END \
_data[(x * 3 + 0) + (y * 3 + 0) * size().x] = out_tl;\
_data[(x * 3 + 1) + (y * 3 + 0) * size().x] = out_t;\
_data[(x * 3 + 2) + (y * 3 + 0) * size().x] = out_tr;\
_data[(x * 3 + 0) + (y * 3 + 1) * size().x] = out_l;\
_data[(x * 3 + 1) + (y * 3 + 1) * size().x] = out_m;\
_data[(x * 3 + 2) + (y * 3 + 1) * size().x] = out_r;\
_data[(x * 3 + 0) + (y * 3 + 2) * size().x] = out_bl;\
_data[(x * 3 + 1) + (y * 3 + 2) * size().x] = out_b;\
_data[(x * 3 + 2) + (y * 3 + 2) * size().x] = out_br;\
}\
}\
break;
int upscaler_scale(upscaler upscaler) {
switch(upscaler) {
case upscaler::none: return 1;
case upscaler::scale2x: return 2;
case upscaler::scale3x: return 3;
case upscaler::lcd3x: return 3;
case upscaler::rgb3x: return 3;
case upscaler::bilinear3x: return 3;
case upscaler::hatch3x: return 3;
}
}
void image::draw_upscaled(image const* img, upscaler upscaler) {
draw_upscaled(img, img->size(), upscaler);
}
static std::vector<image*> scale_buffers;
void image::draw_upscaled(image const* img, std::vector<upscaler> upscalers) {
if(upscalers.size() > scale_buffers.size() + 1)
scale_buffers.resize(upscalers.size() - 1, nullptr);
auto src = img;
auto size = img->size();
for(int i = 0; i + 1 < upscalers.size(); ++i) {
auto osize = size * upscaler_scale(upscalers[i]);
if(!scale_buffers[i] || scale_buffers[i]->size().x < osize.x || scale_buffers[i]->size().y < osize.y) {
delete scale_buffers[i];
scale_buffers[i] = new image(osize);
}
scale_buffers[i]->draw_upscaled(src, size, upscalers[i]);
src = scale_buffers[i];
size = osize;
}
this->draw_upscaled(src, size, upscalers.back());
}
void image::draw_upscaled(image const* img, vec2i s, upscaler upscaler) {
switch(upscaler) {
case upscaler::none:
UPSCALE_1X
out = in;
UPSCALE_1X_END
case upscaler::scale2x:
UPSCALE_2X
out_tl = in_l == in_t && in_t != in_r && in_l != in_b ? in_l : in_m;
out_tr = in_t == in_r && in_t != in_l && in_r != in_b ? in_r : in_m;
out_bl = in_l == in_b && in_l != in_t && in_b != in_r ? in_l : in_m;
out_br = in_b == in_r && in_l != in_b && in_t != in_r ? in_r : in_m;
UPSCALE_2X_END
case upscaler::scale3x:
UPSCALE_3X
out_tl = in_l == in_t && in_l != in_b && in_t != in_r ? in_l : in_m;
out_t = (in_l == in_t && in_l != in_b && in_t != in_r && in_m != in_tr) ||
(in_t == in_r && in_t != in_l && in_r != in_b && in_m != in_tl) ? in_t : in_m;
out_tr = in_t == in_r && in_t != in_l && in_r != in_b ? in_r : in_m;
out_l = (in_b == in_l && in_b != in_r && in_l != in_t && in_m != in_tl) ||
(in_l == in_t && in_l != in_b && in_t != in_r && in_m != in_bl) ? in_l : in_m;
out_m = in_m;
out_r = (in_t == in_r && in_t != in_l && in_r != in_b && in_m != in_br) ||
(in_r == in_b && in_r != in_t && in_b != in_l && in_m != in_tr) ? in_r : in_m;
out_bl = in_b == in_l && in_b != in_r && in_l != in_t ? in_l : in_m;
out_b = (in_r == in_b && in_r != in_t && in_b != in_l && in_m != in_bl) ||
(in_b == in_l && in_b != in_r && in_l != in_t && in_m != in_br) ? in_b : in_m;
out_br = in_r == in_b && in_r != in_t && in_b != in_l ? in_r : in_m;
UPSCALE_3X_END
case upscaler::lcd3x:
UPSCALE_3X
out_tl = out_t = out_l = out_m = in_m;
out_tr = out_r = out_br = out_b = out_bl = (in_m & 0xfefefe) >> 1;
UPSCALE_3X_END
case upscaler::rgb3x:
UPSCALE_3X
out_tl = out_l = out_bl = in_m & 0xff0000;
out_t = out_m = out_b = in_m & 0x00ff00;
out_tr = out_r = out_br = in_m & 0x0000ff;
UPSCALE_3X_END
case upscaler::bilinear3x:
UPSCALE_3X
out_m = in_m;
out_l = ((in_m & 0xfefefe) >> 1) + ((in_l & 0xfefefe) >> 1);
out_r = ((in_m & 0xfefefe) >> 1) + ((in_r & 0xfefefe) >> 1);
out_t = ((in_m & 0xfefefe) >> 1) + ((in_t & 0xfefefe) >> 1);
out_b = ((in_m & 0xfefefe) >> 1) + ((in_b & 0xfefefe) >> 1);
out_tl = ((in_m & 0xfcfcfc) >> 2) + ((in_t & 0xfcfcfc) >> 2) + ((in_l & 0xfcfcfc) >> 2) + ((in_tl & 0xfcfcfc) >> 2);
out_tr = ((in_m & 0xfcfcfc) >> 2) + ((in_t & 0xfcfcfc) >> 2) + ((in_r & 0xfcfcfc) >> 2) + ((in_tr & 0xfcfcfc) >> 2);
out_bl = ((in_m & 0xfcfcfc) >> 2) + ((in_b & 0xfcfcfc) >> 2) + ((in_l & 0xfcfcfc) >> 2) + ((in_bl & 0xfcfcfc) >> 2);
out_br = ((in_m & 0xfcfcfc) >> 2) + ((in_b & 0xfcfcfc) >> 2) + ((in_r & 0xfcfcfc) >> 2) + ((in_br & 0xfcfcfc) >> 2);
UPSCALE_3X_END
case upscaler::hatch3x:
UPSCALE_3X
out_m = in_m;
out_l = in_l;
out_r = in_r;
out_t = in_t;
out_b = in_b;
out_tl = in_tl;
out_tr = in_tr;
out_bl = in_bl;
out_br = in_br;
UPSCALE_3X_END
}
}
image const* image::upscale_eagle_2x() const {
auto img = new image(size() * 2);
for(int y = 0; y < size().y; ++y) {
for(int x = 0; x < size().x; ++x) {
color in_t, in_b, in_l, in_r, in_m, out_tl, out_tr, out_bl, out_br;
in_t = get_safe(vec2i(x, y - 1));
in_b = get_safe(vec2i(x, y + 1));
in_l = get_safe(vec2i(x - 1, y));
in_r = get_safe(vec2i(x + 1, y));
in_m = get(vec2i(x, y));
out_tl = in_l == in_t && in_t != in_r && in_l != in_b ? in_l : in_m;
out_tr = in_t == in_r && in_t != in_l && in_r != in_b ? in_r : in_m;
out_bl = in_l == in_b && in_l != in_t && in_b != in_r ? in_l : in_m;
out_br = in_b == in_r && in_l != in_b && in_t != in_r ? in_r : in_m;
img->_data[(x * 2 + 0) + (y * 2 + 0) * img->size().x] = out_tl;
img->_data[(x * 2 + 1) + (y * 2 + 0) * img->size().x] = out_tr;
img->_data[(x * 2 + 0) + (y * 2 + 1) * img->size().x] = out_bl;
img->_data[(x * 2 + 1) + (y * 2 + 1) * img->size().x] = out_br;
}
}
return img;
}

View File

@ -96,7 +96,13 @@ void game::run() {
screen.draw(_cursor, _mouse_pos);
auto p = screen.raw_pointer();
if(screen.bounds() != window_image.bounds()) {
window_image.draw_upscaled(&screen);
//window_image.draw_upscaled(&screen);
std::vector<upscaler> scalers;
//scalers.push_back(upscaler::scale3x);
//scalers.push_back(upscaler::scale3x);
scalers.push_back(upscaler::lcd3x);
//scalers.push_back(upscaler::hatch3x);
window_image.draw_upscaled(&screen, scalers);
p = window_image.raw_pointer();
}

View File

@ -66,6 +66,12 @@ color image::get(vec2i pos) const {
return _data[pos.x + pos.y * _bounds.size.x];
}
color image::get_safe(vec2i pos) const {
if(pos.x < 0 || pos.y < 0 || pos.x >= _bounds.size.x || pos.y >= _bounds.size.y)
return 0x00000000;
return _data[pos.x + pos.y * _bounds.size.x];
}
void image::clear(color color) {
std::fill(_data, _data + size().size(), color);
}

View File

@ -7,7 +7,7 @@ std::string const& ini_category::get_string(std::string const& entry) const {
if(_data.contains(entry))
return _data.at(entry);
return "";
return std::string("");
}
ini::ini(resource_manager& rm, ini_category const* ini, std::string const& path, std::vector<uint8_t> data) {

BIN
test/assets/slime.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

View File

@ -7,6 +7,7 @@ struct my_game : game {
void render(image& target);
image const* img;
image const* slime;
ini const* ini;
@ -16,28 +17,36 @@ struct my_game : game {
};
void my_game::init(settings& settings) {
settings.target_fps = 60000;
settings.scale = 1;
settings.size = vec2i(1920, 1080);
settings.target_fps = 6000;
time = 0.0;
settings.scale = 3;
settings.size = vec2i(1920/3, 1080/3);
img = get<image>("test.png");
slime = get<image>("slime.png");
//slime = slime->upscale_2x();
//slime = slime->upscale_2x();
//slime = slime->upscale_2x();
//ini = get<::ini>("test.ini");
font = get<::font>("_/default-font-prop.png");
//cursor(&font['\\']);
}
void my_game::update() {
time += 1.0 / 60.0;
time = std::fmod(time + 1.0 / 60.0, 1.0);
}
void my_game::render(image& target) {
target.clear(0xffdddd);
auto pos = vec2i(std::sin(time * TAU / 4) * 100, std::cos(time * TAU / 4) * 100);
//target.draw(img, vec2i(320, 180) + pos, recti(vec2i(8, 14), vec2i(8 * 4, 14 * 4)));
for(int i = 0; i < 1; ++i)
target.draw(img, vec2i::zero);
//for(int i = 0; i < 1; ++i)
// target.draw(img, vec2i::zero);
//target.draw_rot(slime, vec2i(200, 200), time);
target.draw(slime, vec2i(40, 40));
//target.draw(tileset[0xda], vec2i(320, 180) + pos, 0xff00ff);
auto str = "Can only be played if\nthere are no card in\nyour draw pile.\nDeal 50 damage to ALL\nenemies.";
target.draw(str, vec2i(100, 100) + pos, font, 0xffffff, 0);
//target.draw(str, vec2i(100, 100) + pos, font, 0xffffff, 0);
target.draw(std::format("FPS: {}", fps()), vec2i(10, 10), font, 0xffffff, 0);
//target.draw("\\", mouse_pos(), font, 0xffffff, 0);
}