|
//==============================================================================================
#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"
#include <array>
#include <stdio.h>
#include <conio.h> // _kbhit
#include <Windows.h>
//------------------------------+---------------------------------------------------------------
#define CRE // constructor mark
using u8 = unsigned char;
using u16 = unsigned short;
using u32 = unsigned int;
using uu = std::size_t;
//------------------------------+---------------------------------------------------------------
constexpr auto make_color16( u8 l, u8 r, u8 g, u8 b )
{
return u8( l << 3 | r << 2 | g << 1 | b );
}
enum class C4 : u8
{
// + R G B
black = make_color16( 0, 0, 0, 0 ),
navy = make_color16( 0, 0, 0, 1 ),
green = make_color16( 0, 0, 1, 0 ),
teal = make_color16( 0, 0, 1, 1 ),
maroon = make_color16( 0, 1, 0, 0 ),
purple = make_color16( 0, 1, 0, 1 ),
olive = make_color16( 0, 1, 1, 0 ),
silver = make_color16( 0, 1, 1, 1 ),
gray = make_color16( 1, 0, 0, 0 ),
blue = make_color16( 1, 0, 0, 1 ),
lime = make_color16( 1, 0, 1, 0 ),
aqua = make_color16( 1, 0, 1, 1 ),
red = make_color16( 1, 1, 0, 0 ),
fuchsia = make_color16( 1, 1, 0, 1 ),
yellow = make_color16( 1, 1, 1, 0 ),
white = make_color16( 1, 1, 1, 1 ),
};
constexpr C4 gray_table[]
{
C4::black, C4::gray, C4::gray, C4::gray, C4::gray, C4::gray, C4::gray, C4::silver,
C4::gray, C4::silver,C4::silver,C4::silver,C4::silver,C4::silver,C4::silver,C4::white,
};
constexpr auto operator/( C4 fore, C4 back )
{
return u8( static_cast< u8 >( back ) << 4 | static_cast< u8 >( fore ) );
};
auto& operator++( C4& c )
{
return c == C4::black ? c = C4::gray :
c == C4::gray ? c = C4::silver :
c == C4::silver ? c = C4::white :
c = static_cast< C4 > ( static_cast< u8 >( c ) | 8 );
};
auto& operator--( C4& c )
{
return c == C4::gray ? c = C4::black :
c == C4::silver ? c = C4::gray :
c == C4::white ? c = C4::silver :
c = static_cast< C4 > ( static_cast< u8 >( c ) & 7 );
};
constexpr auto get_fore( u8 color )
{
return static_cast< C4 >( color & 0xF );
}
constexpr auto get_back( u8 color )
{
return static_cast< C4 >( color >> 4 );
}
//------------------------------+---------------------------------------------------------------
struct TEX
{
u8 color;
char letter;
CRE TEX() = default;
CRE TEX( const u8 color, const char c )
: color( color ), letter( c ) {}
CRE TEX( const int value )
: color( value & 0xFF ), letter( ( value & 0xFF00 ) >> 8 ) {}
};
//------------------------------+---------------------------------------------------------------
template< int x_size = 80, int y_size = 25 >
class PAGE
{
static const int size = x_size * y_size;
using LINE = TEX( & )[ x_size ];
using MATRIX = TEX( & )[ y_size ][ x_size ];
public:
CRE PAGE() = default;
CRE PAGE( const PAGE& page )
{
buffer = page.buffer;
}
std::array< TEX, size > buffer;
MATRIX& cells = (MATRIX&)buffer;
u8 text_color = C4::silver / C4::black;
u8 number_color = C4::silver / C4::black;
u8 number_size = (u8)11;
bool right_alignment = true;
auto xsize() const { return x_size; }
auto ysize() const { return y_size; }
void fill( const TEX tex )
{
buffer.fill( tex );
}
void put( const int x, const int y, const TEX tex )
{
cells[ y ][ x ] = tex;
}
void put( const int x, const int y, const char c )
{
put( x, y, { text_color, c } );
}
auto puts( const int x, const int y, const char* str )
{
int i;
for( i = 0; str[ i ]; ++i )
put( x + i, y, str[ i ] );
return x + i;
}
template< class T, uu SIZE >
auto puts( const int x, const int y, const char* str,
const T( &colors )[ SIZE ], const int offset = 0 )
{
int i;
for( i = 0; str[ i ]; ++i )
put( x + i, y,
{ static_cast< u8 >( colors[ ( i + offset ) % SIZE ] ), str[ i ] } );
return x + i;
}
auto to_string( const int n ) const
{
static char numstr[ 14 ];
int* c = (int*)numstr;
c[ 2 ] = c[ 1 ] = c[ 0 ] = 0x20202020;
bool neg = false;
u32 u = n;
if( n < 0 ) neg = true, u = -n;
char* p = numstr + ( sizeof numstr - 1 );
char* end = p - number_size;
do *--p = u % 10 + '0', u /= 10; while( u );
if( neg ) *--p = '-';
if( !right_alignment )
return p;
return end;
}
auto text_number( const int x, const int y,
const char* s, const int n )
{
return puts( puts( x, y, s ), y, to_string( n ) );
}
void horz_line( const int left, const int right,
const int y, const TEX tex )
{
for( int x = left; x < right; ++x )
put( x, y, tex );
}
void vert_line( const int x, const int top,
const int bottom, const TEX tex )
{
for( int y = top; y < bottom; ++y )
put( x, y, tex );
}
void rectangle( const int left, const int top,
const int right, const int bottom, TEX tex )
{
const int y2 = bottom - 1;
horz_line( left, right, top, tex );
horz_line( left, right, y2, tex );
for( int y = top + 1; y < y2; ++y )
horz_line( left, right, y, tex );
}
void frame( const int left, const int top,
const int right, const int bottom, TEX tex )
{
const int y2 = bottom - 1;
horz_line( left, right, top, tex );
horz_line( left, right, y2, tex );
vert_line( left, top + 1, y2, tex );
vert_line( right - 1, top + 1, y2, tex );
}
void frame_bold( const int left, const int top,
const int right, const int bottom, TEX tex )
{
const int y2 = bottom - 1;
frame( left, top, right, bottom, tex );
vert_line( left + 1, top + 1, y2, tex );
vert_line( right - 2, top + 1, y2, tex );
}
template< int w, int h >
void draw( const int left, const int top, const PAGE< w, h >& page )
{
const int x_ = max( 0, -left );
const int y_ = max( 0, -top );
const int w_ = min( w, x_size - left );
const int h_ = min( h, y_size - top );
for( int y = y_; y < h_; ++y )
{
const int yt = y + top;
for( int x = x_; x < w_; ++x )
if( page.cells[ y ][ x ].letter )
put( x + left, yt, page.cells[ y ][ x ] );
}
}
void to_gray()
{
for( auto& tex : buffer )
tex.color = gray_table[ static_cast< u8 >( get_fore( tex.color ) ) ] /
gray_table[ static_cast< u8 >( get_back( tex.color ) ) ];
}
void to_lighter()
{
for( auto& tex : buffer )
{
auto fore = get_fore( tex.color );
auto back = get_back( tex.color );
tex.color = ++fore / ++back;
}
}
void to_darker()
{
for( auto& tex : buffer )
{
auto fore = get_fore( tex.color );
auto back = get_back( tex.color );
tex.color = --fore / --back;
}
}
};
//------------------------------+---------------------------------------------------------------
template< int x_size = 80, int y_size = 25 >
class SCREEN
{
char buffer[ 8192 ];
HANDLE handle;
void go( const short x, const short y ) const
{
fflush( stdout );
SetConsoleCursorPosition( handle, { x, y } );
}
auto color( const u8 c ) const
{
fflush( stdout );
SetConsoleTextAttribute( handle, c );
return c;
}
void resize( short w, short h ) const
{
COORD coord { w, h };
SetConsoleScreenBufferSize( handle, coord );
SMALL_RECT rect = { 0,0, w - 1, h - 1 };
SetConsoleWindowInfo( handle, TRUE, &rect );
}
public:
PAGE< x_size, y_size > top_page;
CRE SCREEN( bool full_screen = false )
: handle( GetStdHandle( STD_OUTPUT_HANDLE ) )
{
setvbuf( stdout, (char*)&buffer, _IOFBF, sizeof buffer );
if( full_screen )
SetConsoleDisplayMode( handle, CONSOLE_FULLSCREEN_MODE, NULL );
SetConsoleMode( handle, 0x0008 ); // DISABLE_NEWLINE_AUTO_RETURN
cursor( false );
}
auto xsize() const { return x_size; }
auto ysize() const { return y_size; }
void cursor( const bool show ) const
{
CONSOLE_CURSOR_INFO info = { 1, show };
SetConsoleCursorInfo( handle, &info );
}
void flush()
{
resize( x_size, y_size );
u8 old_color = color( top_page.buffer[ 0 ].color );
for( int y = 0; y < y_size; ++y )
{
go( 0, y );
TEX* scanline = top_page.cells[ y ];
for( int x = 0; x < x_size; ++x )
{
if( old_color != scanline[ x ].color )
old_color = color( scanline[ x ].color );
putchar( scanline[ x ].letter );
}
}
fflush( stdout );
}
void fadeout( const int delay = 1000 )
{
top_page.to_gray();
flush();
for( int i = 0; i < 4; ++i )
{
::Sleep( delay );
top_page.to_lighter();
flush();
}
}
void fadein( const int delay = 1000 )
{
top_page.to_gray();
flush();
for( int i = 0; i < 4; ++i )
{
::Sleep( delay );
top_page.to_darker();
flush();
}
}
template< class F = void(*)() >
void waitfor_press( F f = []{} )
{
while( !_kbhit() ) f();
getchar();
}
};
//------------------------------+---------------------------------------------------------------
// 여기서부터 데모 예제
SCREEN<> stage;
int main()
{
stage.top_page.fill( { C4::silver / C4::black, ' ' } );
stage.top_page.frame_bold( 0, 0, stage.top_page.xsize(), stage.top_page.ysize(),
{ C4::black / C4::blue, ' ' } );
const u8 color_set1[] = { 15, 7, 8 };
stage.top_page.puts( 35, 2, "Hello teX", color_set1 );
const C4 color_set2[] = { C4::red, C4::white, C4::white, C4::white };
stage.top_page.puts( 4, 4, "Codesafer's C++ programming guide", color_set2 );
PAGE<> back( stage.top_page );
stage.waitfor_press( [ &color_set2 ]
{
static int i = 0;
stage.top_page.puts( 4, 6, "Codesafer's C++ programming guide", color_set2, i++ );
stage.flush();
} );
stage.fadeout();
PAGE< 4, 4 > stamp;
stamp.rectangle( 0, 0, 4, 4, { C4::white / C4::blue, ' ' } );
for( int y = 0; y < stage.ysize(); y += stamp.ysize() )
for( int x = 0; x < stage.xsize(); x += stamp.xsize() )
{
stage.top_page.draw( x, y, stamp );
stage.flush();
::Sleep( 3 );
}
int frames = 0;
stage.waitfor_press( [ &frames ]
{
for( int i = (int)C4::blue; i < 256; i += 16 )
{
stage.top_page.text_color = (u8)i;
stage.top_page.fill( i );
stage.top_page.text_number( 30, 12, "Hello teX", ++frames );
stage.flush();
::Sleep( 30 );
}
} );
stage.fadein();
for( int y = 0; y < back.ysize(); ++y )
{
stage.top_page.draw( 0, y, back );
stage.flush();
::Sleep( 16 );
}
for( int y = back.ysize(); y >= -back.ysize(); --y )
{
stage.top_page.draw( 0, y, back );
stage.flush();
::Sleep( 16 );
}
for( int y = -back.ysize(); y < 0; ++y )
{
stage.top_page.draw( 0, y, back );
stage.flush();
::Sleep( 16 );
}
for( int x = 0; x < back.xsize(); ++x )
{
stage.top_page.draw( x, 0, back );
stage.flush();
::Sleep( 16 );
}
for( int x = back.xsize(); x >= -back.xsize(); --x )
{
stage.top_page.draw( x, 0, back );
stage.flush();
::Sleep( 16 );
}
stage.waitfor_press();
return 0;
}
//==============================================================================================
2. SCREEN 과 PAGE 로 분리해서 이미지는 PAGE 에서 작업하고 복수의 PAGE 를 사용가능하며,
SCREEN 의 top_page 에 그려주면 되는 식.
3. fade-in / fade-out 적용. ( grayscale 일때 제대로임 )
4. 따라서 demo 늘어남
5. 우측 최하단에 문자 찍으면 스크롤 밀리던 문제 해결
6. 함수 다 테스트 안해봄~ ( 널문자는 투명으로 처리하게 해 뒀는데 테스트 안해봄 )
갖고 노실분 갖고 노십쇼~