roflcat/include/color_printer_true.hpp

200 lines
6.6 KiB
C++

/**
roflcat
Copyright (C) 2019 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COLOR_PRINTER_TRUE_HPP
#define COLOR_PRINTER_TRUE_HPP
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE //for wcwidth
#endif
#include <cwchar> //wcwidth
#include "cmd.hpp"
#include "color_printer_base.hpp"
#include <cstdlib> //std::abs
#include <algorithm> //std::min, std::max
#include <cstdio> //wprintf, putwchar
#include <cstdlib> //rand
//True color printer. Too many options to enumerate the entire list of possible colors, so we calculate on the fly
class color_printer_true : public color_printer_base<color_printer_true>
{
friend class printer_base<color_printer_true>;
private:
static constexpr const char* lookup_table[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"};
static constexpr long lookup_table_len = sizeof(lookup_table)/sizeof(lookup_table[0]);
static constexpr int color_min_amount = 20;
static constexpr int color_max_amount = 255;
enum class color{
red, green, blue
};
struct color_state{
int r = color_min_amount, g = color_min_amount, b = color_max_amount;
color curr = color::red;
color_state(void)noexcept{
switch(rand()%3){
case 0:
r = color_max_amount;
g = rand()%(color_max_amount-color_min_amount) + color_min_amount;
b = color_min_amount;
curr = color::green;
break;
case 1:
r = color_min_amount;
g = color_max_amount;
b = rand()%(color_max_amount-color_min_amount) + color_min_amount;
curr = color::blue;
break;
case 2:
r = rand()%(color_max_amount-color_min_amount) + color_min_amount;
g = color_min_amount;
b = color_max_amount;
curr = color::red;
break;
};
}
constexpr color inc(void){
switch(curr){
default:
case color::red:
return (curr = color::green);
case color::green:
return (curr = color::blue);
case color::blue:
return (curr = color::red);
};
}
constexpr color dec(void){
switch(curr){
default:
case color::red:
return (curr = color::blue);
case color::green:
return (curr = color::red);
case color::blue:
return (curr = color::green);
};
}
}m_line_color = {}, m_color = m_line_color;
public:
color_printer_true(const cmd_args& args)noexcept:
color_printer_base<color_printer_true>(args, args.invert ? L"\033[38;5;0m\033[48;2;%s;%s;%sm" : L"\033[38;2;%s;%s;%sm"){}
protected:
color_printer_true& _print(const wchar_t s)noexcept{
bool updated = false;
if(s == L'\n'){
inc_line();
updated = true;
if(m_invert)
reset();
putwchar(L'\n');
return *this;
}else{
int width = wcwidth(s);
if(width > 0)
updated = inc_color(wcwidth(s));
else
updated = inc_color(1);
}
if(updated)
wprintf(m_color_str, lookup_table[m_color.r], lookup_table[m_color.g], lookup_table[m_color.b]);
putwchar(s);
return *this;
}
color_printer_true& _reset(void){
return color_printer_base<color_printer_true>::_reset();
}
constexpr void inc_line(void){
int amount = m_spread*(float{color_max_amount}/4);
_do_inc_color(amount, m_line_color);
m_color = m_line_color;
}
constexpr bool inc_color(int i){
int amount = i*m_freq*(float{color_max_amount}/4);
if(amount == 0)
return false;
_do_inc_color(amount, m_color);
return true;
}
constexpr static void _do_inc_color(int amount, color_state& c){
int* curr = nullptr, *next = nullptr, *prev = nullptr;
color (color_state::*inc)(void) = nullptr;
if(amount > 0){
inc = &color_state::inc;
switch(c.curr){
default:
case color::red:
curr = &c.r;
prev = &c.b;
next = &c.g;
break;
case color::green:
curr = &c.g;
prev = &c.r;
next = &c.b;
break;
case color::blue:
curr = &c.b;
prev = &c.g;
next = &c.r;
break;
};
}else{
inc = &color_state::dec;
switch(c.curr){
default:
case color::red:
curr = &c.b;
prev = &c.r;
next = &c.g;
break;
case color::green:
curr = &c.r;
prev = &c.g;
next = &c.b;
break;
case color::blue:
curr = &c.g;
prev = &c.b;
next = &c.r;
break;
};
}
amount = std::abs(amount);
while(amount != 0){
int old = *curr;
*curr = std::min((*curr)+amount, color_max_amount);
amount = (old+amount)-(*curr);
old = *prev;
*prev = std::max((*prev)-amount, color_min_amount);
amount = (amount-old)+(*prev);
if((*prev) == color_min_amount){
(c.*inc)();
int* tmp = curr;
curr = next;next = prev;prev = tmp;
}
}
}
};
#endif