ANSI code renderer starts working but I have to make it utf8/wchar_t friendly.

main
Zed A. Shaw 3 weeks ago
parent 6ca4614fcb
commit ae484bf425
  1. 128
      ansi_parser.cpp
  2. 5
      ansi_parser.hpp
  3. 46
      ansi_parser.rl
  4. 8
      map.hpp
  5. 13
      meson.build
  6. 54
      render.cpp
  7. 1
      render.hpp
  8. 4
      systems.cpp
  9. 14
      tests/ansi_parser.cpp

@ -5,105 +5,108 @@
#include <charconv>
#include "dbc.hpp"
#include <SFML/Graphics.hpp>
#include "ansi_parser.hpp"
using namespace fmt;
#line 62 ".\\ansi_parser.rl"
#line 59 ".\\ansi_parser.rl"
#line 12 ".\\ansi_parser.cpp"
#line 13 ".\\ansi_parser.cpp"
static const char _foo_actions[] = {
0, 1, 0, 1, 3, 1, 4, 1,
5, 1, 9, 1, 10, 2, 1, 6,
2, 1, 7, 2, 10, 5, 3, 1,
8, 2
5, 1, 6, 1, 7, 1, 11, 1,
12, 2, 1, 8, 2, 1, 9, 2,
12, 5, 3, 1, 10, 2
};
static const char _foo_key_offsets[] = {
0, 0, 1, 2, 4, 6, 7, 8,
9, 11, 14, 16, 19, 21, 24, 25,
27, 28, 29, 30
27, 28, 29, 30, 31
};
static const char _foo_trans_keys[] = {
27, 91, 51, 52, 56, 57, 59, 50,
59, 48, 57, 59, 48, 57, 48, 57,
59, 48, 57, 48, 57, 109, 48, 57,
109, 56, 57, 59, 50, 27, 27, 0
109, 56, 57, 59, 50, 109, 27, 27,
0
};
static const char _foo_single_lengths[] = {
0, 1, 1, 2, 2, 1, 1, 1,
0, 1, 0, 1, 0, 1, 1, 2,
1, 1, 1, 1
1, 1, 1, 1, 1
};
static const char _foo_range_lengths[] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0
0, 0, 0, 0, 0
};
static const char _foo_index_offsets[] = {
0, 0, 2, 4, 7, 10, 12, 14,
16, 18, 21, 23, 26, 28, 31, 33,
36, 38, 40, 42
36, 38, 40, 42, 44
};
static const char _foo_trans_targs[] = {
2, 1, 3, 0, 4, 15, 0, 5,
14, 0, 6, 0, 7, 0, 8, 0,
9, 0, 10, 9, 0, 11, 0, 12,
11, 0, 13, 0, 19, 13, 0, 19,
0, 16, 14, 0, 17, 0, 7, 0,
2, 1, 2, 1, 0
11, 0, 13, 0, 20, 13, 0, 20,
0, 16, 18, 0, 17, 0, 7, 0,
20, 0, 2, 1, 2, 1, 0
};
static const char _foo_trans_actions[] = {
0, 7, 0, 0, 9, 9, 0, 0,
0, 7, 0, 0, 13, 13, 0, 0,
0, 0, 0, 0, 3, 0, 0, 0,
1, 0, 13, 0, 0, 1, 0, 16,
0, 0, 1, 0, 22, 0, 0, 0,
1, 0, 17, 0, 0, 1, 0, 20,
0, 0, 1, 0, 26, 0, 0, 9,
0, 0, 0, 0, 0, 0, 5, 0,
0, 7, 11, 19, 0
11, 0, 0, 7, 15, 23, 0
};
static const char _foo_eof_actions[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 11
0, 0, 0, 0, 15
};
static const int foo_start = 18;
static const int foo_first_final = 18;
static const int foo_start = 19;
static const int foo_first_final = 19;
static const int foo_error = 0;
static const int foo_en_main = 18;
static const int foo_en_main = 19;
#line 65 ".\\ansi_parser.rl"
#line 62 ".\\ansi_parser.rl"
bool parse_ansi(std::string_view codes) {
bool parse_ansi(std::string_view codes, sf::Color default_fg, sf::Color default_bg, WriteCB write) {
const char *start = NULL;
int cs = 0;
unsigned int value = 0;
const char *p = codes.data();
const char *pe = p + codes.size();
const char *eof = pe;
bool is_fg = false;
sf::Color color;
sf::Color bgcolor(default_bg);
sf::Color color(default_fg);
sf::Color &target = bgcolor;
#line 91 ".\\ansi_parser.cpp"
#line 94 ".\\ansi_parser.cpp"
{
cs = foo_start;
}
#line 77 ".\\ansi_parser.rl"
#line 75 ".\\ansi_parser.rl"
#line 94 ".\\ansi_parser.cpp"
#line 97 ".\\ansi_parser.cpp"
{
int _klen;
unsigned int _trans;
@ -177,62 +180,65 @@ _match:
switch ( *_acts++ )
{
case 0:
#line 12 ".\\ansi_parser.rl"
#line 13 ".\\ansi_parser.rl"
{
start = p;
}
break;
case 1:
#line 16 ".\\ansi_parser.rl"
#line 17 ".\\ansi_parser.rl"
{
auto [ptr, ec] = std::from_chars(start, p, value);
dbc::check(ec == std::errc(), "error in number parsing");
println("NUM: {}", value);
}
break;
case 2:
#line 22 ".\\ansi_parser.rl"
{
println("color24b");
}
{ }
break;
case 3:
#line 26 ".\\ansi_parser.rl"
{ is_fg = true; }
#line 23 ".\\ansi_parser.rl"
{ target = color; }
break;
case 4:
#line 27 ".\\ansi_parser.rl"
{ is_fg = false; }
#line 24 ".\\ansi_parser.rl"
{ target = bgcolor; }
break;
case 5:
#line 29 ".\\ansi_parser.rl"
#line 26 ".\\ansi_parser.rl"
{
println("ANY: {}:{}", int((*p)), (*p));
write(bgcolor, color, (*p));
}
break;
case 6:
#line 33 ".\\ansi_parser.rl"
{ color.r = value; }
#line 30 ".\\ansi_parser.rl"
{ color = default_fg; }
break;
case 7:
#line 34 ".\\ansi_parser.rl"
{ color.g = value; }
#line 31 ".\\ansi_parser.rl"
{ bgcolor = default_bg; }
break;
case 8:
#line 35 ".\\ansi_parser.rl"
{ color.b = value; }
#line 33 ".\\ansi_parser.rl"
{ target.r = value; }
break;
case 9:
#line 37 ".\\ansi_parser.rl"
{ value = 0; }
#line 34 ".\\ansi_parser.rl"
{ target.g = value; }
break;
case 10:
#line 39 ".\\ansi_parser.rl"
{
println("fg? {}, sf:Color: {},{},{},{}", is_fg, color.r, color.g, color.b, color.a);
}
#line 35 ".\\ansi_parser.rl"
{ target.b = value; }
break;
#line 211 ".\\ansi_parser.cpp"
case 11:
#line 36 ".\\ansi_parser.rl"
{ value = 0; }
break;
case 12:
#line 37 ".\\ansi_parser.rl"
{ }
break;
#line 215 ".\\ansi_parser.cpp"
}
}
@ -248,13 +254,11 @@ _again:
unsigned int __nacts = (unsigned int) *__acts++;
while ( __nacts-- > 0 ) {
switch ( *__acts++ ) {
case 10:
#line 39 ".\\ansi_parser.rl"
{
println("fg? {}, sf:Color: {},{},{},{}", is_fg, color.r, color.g, color.b, color.a);
}
case 12:
#line 37 ".\\ansi_parser.rl"
{ }
break;
#line 231 ".\\ansi_parser.cpp"
#line 233 ".\\ansi_parser.cpp"
}
}
}
@ -262,9 +266,7 @@ _again:
_out: {}
}
#line 78 ".\\ansi_parser.rl"
print("PROCESSED {} CHARS of {}: {}", p - codes.data(), codes.size(), p);
#line 76 ".\\ansi_parser.rl"
return true;
return p - pe == 0;
}

@ -1,4 +1,7 @@
#pragma once
#include <string_view>
#include <SFML/Graphics.hpp>
bool parse_ansi(std::string_view codes);
typedef std::function<void(sf::Color bgcolor, sf::Color color, char ch)> WriteCB;
bool parse_ansi(std::string_view codes, sf::Color default_fg, sf::Color default_bg, WriteCB write);

@ -3,6 +3,7 @@
#include <charconv>
#include "dbc.hpp"
#include <SFML/Graphics.hpp>
#include "ansi_parser.hpp"
using namespace fmt;
@ -16,34 +17,30 @@ using namespace fmt;
action number {
auto [ptr, ec] = std::from_chars(start, fpc, value);
dbc::check(ec == std::errc(), "error in number parsing");
println("NUM: {}", value);
}
action color24b {
println("color24b");
}
action is_fg { is_fg = true; }
action is_bg { is_fg = false; }
action color24b { }
action is_fg { target = color; }
action is_bg { target = bgcolor; }
action any {
println("ANY: {}:{}", int(fc), fc);
action out {
write(bgcolor, color, fc);
}
action red { color.r = value; }
action blue { color.g = value; }
action green { color.b = value; }
action reset_fg { color = default_fg; }
action reset_bg { bgcolor = default_bg; }
action red { target.r = value; }
action blue { target.g = value; }
action green { target.b = value; }
action start { value = 0; }
action end { }
action end {
println("fg? {}, sf:Color: {},{},{},{}", is_fg, color.r, color.g, color.b, color.a);
}
start = 0x1B "[";
ESC = 0x1B;
start = ESC "[";
fg = "38;" %is_fg;
bg = "48;" %is_bg;
reset = ("39" | "49");
reset = ("39" %reset_fg | "49" %reset_bg);
num = digit+ >tstart %number;
color256 = "5;";
color24b = "2;";
@ -56,27 +53,26 @@ using namespace fmt;
) "m" %end
);
other = (any+ @any -- 0x1B)*;
other = (any+ @out -- ESC)*;
main := (other :> ansi)**;
}%%
%% write data;
bool parse_ansi(std::string_view codes) {
bool parse_ansi(std::string_view codes, sf::Color default_fg, sf::Color default_bg, WriteCB write) {
const char *start = NULL;
int cs = 0;
unsigned int value = 0;
const char *p = codes.data();
const char *pe = p + codes.size();
const char *eof = pe;
bool is_fg = false;
sf::Color color;
sf::Color bgcolor(default_bg);
sf::Color color(default_fg);
sf::Color &target = bgcolor;
%% write init;
%% write exec;
print("PROCESSED {} CHARS of {}: {}", p - codes.data(), codes.size(), p);
return true;
return p - pe == 0;
}

@ -11,10 +11,10 @@
#define INV_SPACE 1
#define WALL_VALUE 1
#define SPACE_VALUE 0
#define WALL_TILE ""
#define FLOOR_TILE "#"
#define PLAYER_TILE ""
#define ENEMY_TILE "Ω"
#define WALL_TILE "#"
#define FLOOR_TILE "."
#define PLAYER_TILE "&"
#define ENEMY_TILE "!"
struct Room {
size_t x = 0;

@ -21,12 +21,12 @@ runtests = executable('runtests', [
'collider.cpp',
'ansi_parser.cpp',
'render.cpp',
#'tests/fsm.cpp',
#'tests/dbc.cpp',
#'tests/map.cpp',
#'tests/collider.cpp',
#'tests/sound.cpp',
#'tests/dinkyecs.cpp',
'tests/fsm.cpp',
'tests/dbc.cpp',
'tests/map.cpp',
'tests/collider.cpp',
'tests/sound.cpp',
'tests/dinkyecs.cpp',
'tests/ansi_parser.cpp',
],
dependencies: dependencies)
@ -41,6 +41,7 @@ roguish = executable('roguish', [
'collider.cpp',
'combat.cpp',
'systems.cpp',
'ansi_parser.cpp',
'render.cpp',
],
dependencies: dependencies)

@ -1,5 +1,7 @@
#include "render.hpp"
#include "ansi_parser.hpp"
#include <cmath>
#include <fmt/core.h>
std::array<sf::Color, 10> VALUES{
sf::Color{1, 4, 2}, // black
@ -66,13 +68,16 @@ bool SFMLRender::resize_map(int new_size) {
}
}
void SFMLRender::draw_screen(bool clear, float map_off_x, float map_off_y) {
if(clear) $window.clear();
void SFMLRender::draw_main_ui() {
std::string screenout = $screen.ToString();
std::wstring main_screen_utf8 = $converter.from_bytes(screenout);
$ui_text.setString(main_screen_utf8);
$window.draw($ui_text);
}
void SFMLRender::draw_screen(bool clear, float map_off_x, float map_off_y) {
if(clear) $window.clear();
draw_main_ui();
std::string map_screenout = $map_screen.ToString();
std::wstring map_screen_utf8 = $converter.from_bytes(map_screenout);
@ -82,23 +87,23 @@ void SFMLRender::draw_screen(bool clear, float map_off_x, float map_off_y) {
// make a copy so we don't modify the cached one
auto bg_sprite = get_text_sprite(L'');
auto bg_bounds = bg_sprite.getLocalBounds();
bg_sprite.setColor(sf::Color(20,20,20));
auto add_sprite = get_text_sprite(L'!');
bool has_add = false;
sf::Color def_fg(color(Value::LIGHT_LIGHT));
sf::Color def_bg(color(Value::BLACK));
for(size_t i = 0; i < map_screen_utf8.size(); i++) {
wchar_t tile = map_screen_utf8[i];
if(tile == L'\n') {
parse_ansi(map_screenout, def_fg, def_bg, [&](sf::Color bg, sf::Color fg, char tile) {
if(tile == '\n') {
// don't bother processing newlines, just skip
y += $line_spacing;
x = GAME_MAP_POS;
} else if(tile == L'\r') {
continue; // skip these, just windows junk
return; // skip these, just windows junk
} else {
// fmt::println("FG: {},{},{},{}; BG: {},{},{},{}; ch: {}",
// fg.r, fg.g, fg.b, fg.a, bg.r, bg.g, bg.b, bg.a, int(tile));
// it's a visual cell
bg_sprite.setPosition({x+map_off_x, y+map_off_y});
sf::Sprite &sprite = get_text_sprite(tile);
bg_sprite.setColor(bg);
// should look into caching all this instead of calcing it each time
auto sp_bounds = sprite.getLocalBounds();
@ -107,38 +112,15 @@ void SFMLRender::draw_screen(bool clear, float map_off_x, float map_off_y) {
auto width_delta = bg_bounds.width > sp_bounds.width ? (bg_bounds.width - sp_bounds.width) / 2 : 0;
auto height_delta = bg_bounds.height > sp_bounds.width ? (bg_bounds.height - sp_bounds.height) / 2 : 0;
// TODO: need to center it inside the bg_sprite
sprite.setPosition({x+width_delta+map_off_x, y+height_delta+map_off_y});
sprite.setColor(fg);
// get the entity combat and make them light gray if dead
if(tile == L'') {
sprite.setColor(sf::Color(80,80,80));
} else if(tile == L'') {
sprite.setColor(sf::Color::Blue);
} else if(tile == L'Ω') {
sprite.setColor(sf::Color::Red);
// HACK: just playing with adding multiple characters for drawing
add_sprite.setColor(sf::Color::Red);
add_sprite.setPosition({x-3,y-3});
has_add = true;
} else if(tile == L'#') {
sprite.setColor(sf::Color(5,5,5));
} else {
sprite.setColor(color(Value::MID));
}
// now draw the background sprite and sprite
// TODO: this can become a standard sprite description
$window.draw(bg_sprite);
$window.draw(sprite);
if(has_add) {
$window.draw(add_sprite);
has_add = false;
}
// next cell
x += $base_glyph.advance;
}
}
});
$window.display();
}

@ -49,6 +49,7 @@ struct SFMLRender {
sf::Color color(Value val);
sf::Sprite &get_text_sprite(wchar_t tile);
bool resize_map(int new_size);
void draw_main_ui();
void draw_screen(bool clear=true, float map_off_x=0.0f, float map_off_y=0.0f);
bool poll_event(sf::Event &event) {

@ -5,10 +5,12 @@
#include "rand.hpp"
#include "collider.hpp"
#include "events.hpp"
#include "ftxui/screen/color.hpp"
using std::string;
using namespace fmt;
using namespace Components;
using ftxui::Color;
#define HEARING_DISTANCE 8
@ -142,7 +144,7 @@ void System::draw_map(DinkyECS::World &world, Map &game_map, ftxui::Canvas &canv
for(size_t y = 0; y < end_y; ++y) {
string tile = walls[start.y+y][start.x+x] == 1 ? WALL_TILE : FLOOR_TILE;
// the 2 and 4 are from ftxui::Canvas since it does a kind of "subpixel" drawing
canvas.DrawText(x * 2, y * 4, tile);
canvas.DrawText(x * 2, y * 4, tile, Color::HSV(100, 10, 10));
}
}

@ -33,7 +33,6 @@ std::string generate_colors() {
count++;
}
array.push_back(hbox(std::move(line)));
break; /// UNDO ME
}
println("MADE {} CHARS", count);
@ -53,7 +52,18 @@ std::string generate_colors() {
TEST_CASE("test out ragel parser", "[gui]") {
std::string colors = generate_colors();
sf::Color default_fg(0,0,0);
sf::Color default_bg(100,100,100);
println("--- PARSING");
REQUIRE(parse_ansi(colors));
bool good = parse_ansi(colors, default_fg, default_bg, [&](sf::Color bgcolor, sf::Color color, char ch) {
bool correct_char = ch == '#' || ch == ' ' || ch == '\n' || ch == '\r';
// println("FG: {},{},{},{}; BG: {},{},{},{}; ch: {}",
// color.r, color.g, color.b, color.a,
// bgcolor.r, bgcolor.g, bgcolor.b, bgcolor.a,
// int(ch));
REQUIRE(correct_char);
});
REQUIRE(good);
}

Loading…
Cancel
Save