LCOV - code coverage report
Current view: top level - src/rendering - person.cc (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 88 88 100.0 %
Date: 2022-06-15 20:16:21 Functions: 10 12 83.3 %
Branches: 47 61 77.0 %

           Branch data     Line data    Source code
       1                 :            : #ifdef RUN_TESTS
       2                 :            : # include <gtest/gtest.h>
       3                 :            : # include <set>
       4                 :            : #endif
       5                 :            : /** @file rendering/person.cc
       6                 :            :  * @brief Defines PersonTransform(), which renders a person animation on the screen.
       7                 :            :  */
       8                 :            : 
       9                 :            : #include <cstring>
      10                 :            : #include "clock.hh"
      11                 :            : #include "color.hh"
      12                 :            : #include "cell.hh"
      13                 :            : #include "256color.hh"
      14                 :            : 
      15                 :            : /** This is the graphics data that gets rendered. It consists of two four-color 16x16 sprites. */
      16                 :            : static constexpr char persondata[] =
      17                 :            : "                      #####     "
      18                 :            : "      ######         #'''''###  "
      19                 :            : "     #''''''##      #'''''''''# "
      20                 :            : "    #'''''''''#     ###'.#.###  "
      21                 :            : "    ###..#.###     #..##.#....# "
      22                 :            : "   #..##.#....#    #..##..#...# "
      23                 :            : "   #..##..#...#     ##...#####  "
      24                 :            : "    ##...#####      ###.....#   "
      25                 :            : "     ##.....#     ##'''##''###  "
      26                 :            : "    #''##''#     #..''''##''#'# "
      27                 :            : "   #''''##''#    #..'''######'.#"
      28                 :            : "   #''''#####     #..####.##.#.#"
      29                 :            : "    #...##.##     .#########''# "
      30                 :            : "    #..'''###     #''######'''# "
      31                 :            : "     #'''''#      #'''#  #'''#  "
      32                 :            : "      #####        ###    ###   ";
      33                 :            : static constexpr unsigned xcoordinates[2] = { 0, 16 };
      34                 :            : static constexpr unsigned person_width    = 16;
      35                 :            : static constexpr unsigned data_width      = 32, data_lines = 16;
      36                 :            : 
      37                 :            : static double walk_speed = 64.0; // pixels per second (64 is normal)
      38                 :            : static double frame_rate = 6.0;  // (6 is normal)
      39                 :            : 
      40                 :     336486 : static double GetStartTime()
      41                 :            : {
      42   [ +  +  +  -  :     336486 :     static double value = GetTime();
                   +  - ]
      43                 :     336486 :     return value;
      44                 :            : }
      45                 :            : 
      46                 :            : /** X-coordinate where the Person is */
      47                 :     313062 : int PersonBaseX(unsigned window_width)
      48                 :            : {
      49                 :     313062 :     double start = GetStartTime();
      50                 :     313062 :     double time_elapsed = GetTime() - start;
      51         [ +  - ]:     313062 :     unsigned walkway_width = window_width + std::max(person_width, window_width/5) + person_width;
      52                 :     313062 :     return unsigned(time_elapsed * walk_speed) % walkway_width - person_width;
      53                 :            : }
      54                 :            : /** Which frame to draw right now */
      55                 :      23424 : static int PersonFrame()
      56                 :            : {
      57                 :      23424 :     double start = GetStartTime();
      58                 :      23424 :     double time_elapsed = GetTime() - start;
      59                 :      23424 :     return unsigned(time_elapsed * frame_rate) % 2;
      60                 :            : }
      61                 :            : 
      62                 :            : /** ColorSlideCache implements smooth color slides in text mode. */
      63                 :            : class ColorSlideCache
      64                 :            : {
      65                 :            :     enum { MaxWidth = 3840 };
      66                 :            :     const unsigned char*  const colors;
      67                 :            :     const unsigned short* const color_positions;
      68                 :            :     const unsigned              color_length;
      69                 :            :     unsigned cached_width;
      70                 :            :     unsigned short cache_color[MaxWidth];
      71                 :            :     unsigned char cache_char[MaxWidth];
      72                 :            : 
      73                 :            : public:
      74                 :            :     /** Saves the desired color positions and proportions of the slide for latter use. */
      75                 :            :     ColorSlideCache(const unsigned char* c, const unsigned short* p, unsigned l)
      76                 :            :         : colors(c), color_positions(p), color_length(l), cached_width(0) {}
      77                 :            : 
      78                 :            :     /** Configures the slide for a particular rendering width. */
      79                 :     619584 :     void SetWidth(unsigned w)
      80                 :            :     {
      81         [ +  + ]:     619584 :         if(w == cached_width) return;
      82                 :          3 :         cached_width = w;
      83                 :          3 :         unsigned char first=0;
      84                 :          3 :         { std::memset(cache_char, 0, sizeof(cache_char));
      85                 :          3 :           std::memset(cache_color, 0, sizeof(cache_color)); }
      86         [ +  + ]:        403 :         for(unsigned x=0; x<w && x<MaxWidth; ++x)
      87                 :            :         {
      88                 :        400 :             unsigned short cur_position = (((unsigned long)x) << 16u) / w;
      89   [ +  +  +  + ]:        477 :             while(first < color_length && color_positions[first] <= cur_position) ++first;
      90                 :        400 :             unsigned long  next_position=0;
      91                 :        400 :             unsigned char  next_value=0;
      92         [ +  + ]:        400 :             if(first < color_length)
      93                 :        394 :                 { next_position = color_positions[first]; next_value = colors[first]; }
      94                 :            :             else
      95                 :          6 :                 { next_position = 10000ul; next_value = colors[color_length-1]; }
      96                 :            : 
      97                 :        400 :             unsigned short prev_position=0;
      98                 :        400 :             unsigned char  prev_value   =next_value;
      99         [ +  - ]:        400 :             if(first > 0)
     100                 :        400 :                 { prev_position = color_positions[first-1]; prev_value = colors[first-1]; }
     101                 :            : 
     102                 :        400 :             float position = (cur_position - prev_position) / float(next_position - prev_position );
     103                 :            :             //static const unsigned char chars[4] = { 0x20, 0xB0, 0xB1, 0xB2 };
     104                 :            :             //unsigned char ch = chars[unsigned(position*4)];
     105                 :        400 :             static const unsigned char chars[2] = { 0,1};
     106                 :        400 :             unsigned char ch = chars[unsigned(position*2)];
     107                 :            :             //static const unsigned char chars[2] = { 0x20, 0xDC };
     108                 :            :             //unsigned char ch = chars[unsigned(position*2)];
     109                 :            :             //unsigned char ch = 'A';
     110                 :            :             //if(prev_value == next_value || ch == 0x20) { ch = 0x20; next_value = 0; }
     111         [ +  + ]:        400 :             if(prev_value == next_value || ch == 0) { ch = 0; next_value = 0; }
     112                 :        400 :             cache_char[x] = ch;
     113                 :        400 :             cache_color[x] = prev_value | (next_value << 8u);
     114                 :            :             //cache[x] = 0x80008741ul;
     115                 :            :         }
     116                 :            :     }
     117                 :            :     /** Retrieves the color data for the given position on the slide.
     118                 :            :      * @param x  Coordinate, must be smaller than the width in SetWidth
     119                 :            :      * @param c1 Out-param: Background color to render
     120                 :            :      * @param c2 Out-param: Foreground color to render, if ch is not blank (0)
     121                 :            :      * @param ch Out-param: Character to render (1 = dither pattern, 0 = black)
     122                 :            :      */
     123                 :     619584 :     inline void Get(unsigned x, unsigned char& ch, unsigned char& c1, unsigned char& c2) const
     124                 :            :     {
     125                 :     619584 :         unsigned short tmp = cache_color[x];
     126                 :     619584 :         ch = cache_char[x];
     127                 :     619584 :         c1 = tmp;
     128                 :     619584 :         c2 = tmp >> 8u;
     129                 :            :     }
     130                 :            : };
     131                 :            : static const unsigned char  slide1_colors[21] = {6,73,109,248,7,7,7,7,7,248,109,73,6,6,6,36,35,2,2,28,22};
     132                 :            : static const unsigned short slide1_positions[21] = {0u,1401u,3711u,6302u,7072u,8192u,16384u,24576u,32768u,33889u,34659u,37250u,39560u,40960u,49152u,50903u,53634u,55944u,57344u,59937u,63981u};
     133                 :            : static const unsigned char  slide2_colors[35] = {248,7,249,250,251,252,188,253,254,255,15,230,229,228,227,11,227,185,186,185,179,143,142,136,100,94,58,239,238,8,236,235,234,233,0};
     134                 :            : static const unsigned short slide2_positions[35] = {0u,440u,1247u,2126u,3006u,3886u,4839u,5938u,6965u,8064u,9750u,12590u,15573u,18029u,19784u,21100u,24890u,27163u,30262u,35051u,35694u,38054u,40431u,41156u,46212u,46523u,50413u,52303u,53249u,54194u,56294u,58815u,61335u,63856u,64696u};
     135                 :            : static ColorSlideCache slide1(slide1_colors, slide1_positions, sizeof(slide1_colors));
     136                 :            : static ColorSlideCache slide2(slide2_colors, slide2_positions, sizeof(slide2_colors));
     137                 :            : 
     138                 :            : /* Renders person on the screen. */
     139                 :     619584 : void PersonTransform(unsigned& bgcolor, unsigned& fgcolor,
     140                 :            :                      unsigned width, unsigned x, unsigned y,
     141                 :            :                      unsigned action_type)
     142                 :            : {
     143                 :            :     // Action_type:
     144                 :            :     //  1 = top of screen (Person, green slide)
     145                 :            :     //  2 = bottom of screen (Status, yellow slide)
     146                 :            :     //  0 = anything else
     147         [ +  - ]:     619584 :     if(bgcolor != Cell{}.fgcolor)
     148                 :            :     {
     149                 :            :         // Only transform lines with white (ansi 7) background
     150                 :     596288 :         return;
     151                 :            :     }
     152                 :            : 
     153                 :    1239168 :     auto GetSlide = [&](ColorSlideCache& slide)
     154                 :            :     {
     155                 :     619584 :         slide.SetWidth(width);
     156                 :     619584 :         unsigned char ch,c1,c2; slide.Get(x,ch,c1,c2);
     157                 :     619584 :         unsigned result = c1;
     158         [ +  + ]:     619584 :         if(ch)
     159                 :            :         {
     160                 :     214896 :             unsigned pos = (x^y)&1;
     161         [ +  + ]:     214896 :             result = pos ? c2 : c1;
     162                 :            :         }
     163                 :     619584 :         return xterm256table[result];
     164                 :     619584 :     };
     165         [ +  + ]:     619584 :     if(action_type <= 1) bgcolor = GetSlide(slide1); // Not bottom of screen
     166         [ +  + ]:     619584 :     if(action_type == 2) bgcolor = GetSlide(slide2); // Is bottom of screen
     167                 :            : 
     168   [ +  +  +  - ]:     619584 :     if(y >= data_lines || action_type != 1) // Stop here if not to render person
     169                 :            :     {
     170                 :            :         return;
     171                 :            :     }
     172                 :            : 
     173                 :            :     //unsigned scrx = x;
     174                 :     309760 :     unsigned basex = PersonBaseX(width);
     175                 :            : #ifdef CLOCK_BACKWARDS
     176                 :            :     basex = width - basex;
     177                 :            : #endif
     178                 :     309760 :     x -= basex;
     179                 :            :     // Person outside view?
     180         [ +  + ]:     309760 :     if(x >= person_width)
     181                 :            :     {
     182                 :            :         return;
     183                 :            :     }
     184                 :            : #ifdef CLOCK_BACKWARDS
     185                 :            :     x = person_width-1-x;
     186                 :            : #endif
     187                 :            : 
     188                 :      23296 :     unsigned frame_number = PersonFrame();
     189                 :      23296 :     unsigned frame_start  = xcoordinates[frame_number];
     190                 :      23296 :     char c = persondata[y*data_width + frame_start + x];
     191   [ +  +  +  +  :      23296 :     switch(c)
                      - ]
     192                 :            :     {
     193                 :       9204 :         case ' ': //bgcolor=fgcolor = Mix(bgcolor, fgcolor, 15,1,16); break;
     194                 :       9204 :                   fgcolor         = Mix(bgcolor, fgcolor, 1,15,16); break;
     195                 :       2997 :         case '.': bgcolor=fgcolor = Mix(bgcolor, 0x000000, 7,1,8); break;
     196                 :       3675 :         case '\'':bgcolor=fgcolor = Mix(bgcolor, 0x000000, 6,2,8); break;
     197                 :       7420 :         case '#': bgcolor=fgcolor = 0x000000; break;
     198                 :            :     }
     199                 :            : }
     200                 :            : 
     201                 :            : #ifdef RUN_TESTS
     202                 :          3 : TEST(person, x_coordinate_varies)
     203                 :            : {
     204                 :          1 :     SetTimeFactor(0.);
     205                 :          1 :     std::set<int> coordinates;
     206                 :          1 :     const unsigned width = 640, duration=25, fps=128;
     207                 :            :     // Simulate 25 seconds of time, window width 640, at framerate 128fps.
     208                 :            :     // We should get every X coordinate covered plus a few outside the screen.
     209         [ +  + ]:       3201 :     for(unsigned frame=0; frame<duration*fps; ++frame)
     210                 :            :     {
     211   [ +  -  +  - ]:       3200 :         coordinates.insert( PersonBaseX(width) );
     212         [ +  - ]:       3200 :         AdvanceTime(1.0 / fps);
     213                 :            :     }
     214                 :          1 :     EXPECT_GT(coordinates.size(), std::size_t(width));
     215         [ +  - ]:          1 :     if(!coordinates.empty())
     216                 :            :     {
     217                 :          1 :         EXPECT_LT(*coordinates.begin(), 0);
     218                 :          2 :         EXPECT_GT(*coordinates.rbegin(),  int(width));
     219                 :            :     }
     220                 :          1 : }
     221                 :          3 : TEST(person, pose_varies)
     222                 :            : {
     223                 :          1 :     SetTimeFactor(0.);
     224                 :          1 :     std::set<int> frames;
     225                 :            :     // Simulate 4 seconds of time, framerate 32fps.
     226                 :            :     // We should get both poses of person.
     227         [ +  + ]:        129 :     for(unsigned frame=0; frame<128; ++frame)
     228                 :            :     {
     229   [ +  -  +  - ]:        128 :         frames.insert( PersonFrame() );
     230         [ +  - ]:        128 :         AdvanceTime(1.0 / 32.0);
     231                 :            :     }
     232                 :          2 :     EXPECT_EQ(frames.size(), 2u);
     233                 :          1 : }
     234                 :            : #endif

Generated by: LCOV version 1.16