VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvdemo.cpp
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gvdemo.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gvdemo.h"
23 #include "gtime.h"
24 #include "gassert.h"
25 #include <stdexcept>
26 
27 namespace data
28 {
29  #include "gvdemodata.h"
30 }
31 
32 /// \class Gv::DemoImp
33 /// A pimple-pattern implementation class for Gv::Demo.
34 ///
36 {
37 public:
39  DemoImp( int , int , Gv::Timezone ) ;
40  void drawScene( CaptureBuffer & b , const CaptureBufferScale & scale ) ;
41  void drawItem( CaptureBuffer & b , const CaptureBufferScale & scale ,
42  const char * image , size_t image_dx , size_t image_dy , int offset_x , int offset_y ,
43  triple_t fg , bool draw_bg = false , size_t lhs_blank = 0U ) ;
44  int hex_value( char c ) ;
45  void jitter() ;
46  void still() ;
47 
48 private:
49  struct Out
50  {
52  Out( Gv::CaptureBuffer & b , const Gv::CaptureBufferScale & scale ,
53  size_t image_dx , size_t image_dy , size_t offset_x , size_t offset_y ,
54  triple_t fg , triple_t bg , bool draw_bg , size_t lhs_blank ) ;
55  void operator()( bool b , size_t ) ;
56  static void clear( Gv::CaptureBuffer & , triple_t ) ;
57  unsigned char * m_row ;
58  unsigned char * m_p ;
59  unsigned char * m_lhs ;
60  unsigned char * m_rhs ;
61  size_t m_linesize ;
62  size_t m_image_dx ;
63  size_t m_offset_x_3 ;
64  size_t m_image_x ;
65  bool m_draw_bg ;
66  triple_t m_fg ;
67  triple_t m_bg ;
68  } ;
69 
70 private:
71  Gv::Timezone m_tz ;
72  std::time_t m_blink ;
73  size_t m_cars_pos ;
74  int m_j[12] ;
75  triple_t m_bg ;
76 } ;
77 
78 // ==
79 
80 Gv::Demo::Demo( int dx , int dy , const std::string & /*dev_config*/ , Gv::Timezone tz ) :
81  m_imp(new DemoImp(dx,dy,tz))
82 {
83 }
84 
86 {
87  delete m_imp ;
88 }
89 
91 {
92  return true ; // rgb
93 }
94 
96 {
97  if( scale.m_dx != 640 || scale.m_dy != 480 || scale.m_buffersize != (scale.m_dx*scale.m_dy*3U) )
98  throw std::runtime_error( "unexpected frame buffer size for demo image" ) ;
99  m_imp->drawScene( b , scale ) ;
100 }
101 
102 // ==
103 
104 Gv::DemoImp::DemoImp( int , int , Gv::Timezone tz ) :
105  m_tz(tz) ,
106  m_blink(0) ,
107  m_cars_pos(0U) ,
108  m_bg(triple_t(255U,240U,240U))
109 {
110 }
111 
112 void Gv::DemoImp::jitter()
113 {
114  for( int i = 0 ; i < 12 ; i++ )
115  m_j[i] = std::rand() % 5 ;
116 }
117 
118 void Gv::DemoImp::still()
119 {
120  for( int i = 0 ; i < 12 ; i++ )
121  m_j[i] = 0 ;
122 }
123 
124 void Gv::DemoImp::drawScene( CaptureBuffer & b , const CaptureBufferScale & scale )
125 {
126  Out::clear( b , m_bg ) ;
127  G::EpochTime now = G::DateTime::now() + m_tz.seconds() ;
128  G::Time time( now ) ;
129  triple_t c[] = { triple_t(0,0,70) , triple_t(50,0,80) , triple_t(70,0,100) } ;
130 
131  // traffic (first)
132  {
133  drawItem( b , scale , data::cars_data , data::cars_dx , data::cars_dy ,
134  270+m_cars_pos , 141 , c[2] , false , m_cars_pos < 212U ? (212U-m_cars_pos) : 0U ) ;
135  m_cars_pos++ ;
136  if( m_cars_pos == 420U )
137  m_cars_pos = 0U ;
138  }
139 
140  // base image (inc. normal face, clock surround)
141  {
142  drawItem( b , scale , data::base_data , data::base_dx , data::base_dy , 65 , 10 , c[0] ) ;
143  }
144 
145  // fingers
146  {
147  int finger_x = 176 ;
148  int finger_y = 376 ;
149  still() ;
150  if( (now.s % 9) == 0 )
151  jitter() ;
152  drawItem( b , scale , data::finger_1_data , data::finger_1_dx , data::finger_1_dy , finger_x+m_j[0] , finger_y+m_j[1] , c[0] ) ;
153  drawItem( b , scale , data::finger_2_data , data::finger_2_dx , data::finger_2_dy , finger_x+m_j[2] , finger_y+m_j[3] , c[0] ) ;
154  drawItem( b , scale , data::finger_3_data , data::finger_3_dx , data::finger_3_dy , finger_x+m_j[4] , finger_y+m_j[5] , c[0] ) ;
155  drawItem( b , scale , data::finger_4_data , data::finger_4_dx , data::finger_4_dy , finger_x+m_j[6] , finger_y+m_j[7] , c[0] ) ;
156  drawItem( b , scale , data::finger_5_data , data::finger_5_dx , data::finger_5_dy , finger_x+m_j[8] , finger_y+m_j[9] , c[0] ) ;
157  drawItem( b , scale , data::finger_6_data , data::finger_6_dx , data::finger_6_dy , finger_x+m_j[10] , finger_y+m_j[11] , c[0] ) ;
158  }
159 
160  // cuckoo
161  {
162  if( time.minutes() == 0 && time.seconds() < 3 )
163  drawItem( b , scale , data::bird_data , data::bird_dx , data::bird_dy , 7 , 120 , c[1] ) ;
164  }
165 
166  // blink
167  {
168  if( m_blink == 0 || now.s == m_blink )
169  {
170  if( now.us < 150000 )
171  drawItem( b , scale , data::blink_data , data::blink_dx , data::blink_dy , 256 , 112 , c[0] , true ) ;
172  else
173  m_blink = now.s + 5 ;
174  }
175  }
176 
177  // grimace
178  {
179  if( (now.s % 19) < 2 )
180  drawItem( b , scale , data::grimace_data , data::grimace_dx , data::grimace_dy , 245 , 160 , c[0] , true ) ;
181  }
182 
183  // clock hands
184  {
185  int minute_index = time.minutes() ;
186  int hour_index = ((time.hours() % 12)*5) + time.minutes()/12 ;
187  G_ASSERT( sizeof(data::minute_hand_data)/sizeof(data::minute_hand_data[0]) == 60 ) ;
188  G_ASSERT( sizeof(data::hour_hand_data)/sizeof(data::hour_hand_data[0]) == 60 ) ;
189  minute_index %= 60 ; hour_index %= 60 ; // just in case
190  drawItem( b , scale , data::minute_hand_data[minute_index] , data::minute_hand_dx , data::minute_hand_dy , 97 , 42 , c[0] ) ;
191  drawItem( b , scale , data::hour_hand_data[hour_index] , data::hour_hand_dx , data::hour_hand_dy , 97 , 42 , c[0] ) ;
192  }
193 }
194 
195 void Gv::DemoImp::drawItem( CaptureBuffer & b , const CaptureBufferScale & scale ,
196  const char * image_rle , size_t image_dx , size_t image_dy , int offset_x , int offset_y ,
197  triple_t fg , bool draw_bg , size_t lhs_blank )
198 {
199  Out out( b , scale , image_dx , image_dy , offset_x , offset_y , fg , m_bg , draw_bg , lhs_blank ) ;
200 
201  for( const char * p = image_rle ; p[0] && p[1] ; p += 2 )
202  {
203  int n = (hex_value(p[0])<<4) + hex_value(p[1]) ;
204  bool v = !( n & 0x80 ) ;
205  size_t length = n & 0x7f ;
206  out( !v , length ) ;
207  }
208 }
209 
210 int Gv::DemoImp::hex_value( char c )
211 {
212  if( c >= '0' && c <= '9' )
213  return c - '0' ;
214  else if( c >= 'A' && c <= 'F' )
215  return (c - 'A') + 10 ;
216  else
217  return 0 ;
218 }
219 
220 // ==
221 
222 Gv::DemoImp::Out::Out( Gv::CaptureBuffer & b , const Gv::CaptureBufferScale & scale ,
223  size_t image_dx , size_t image_dy , size_t offset_x , size_t offset_y ,
224  triple_t fg , triple_t bg , bool draw_bg , size_t lhs_blank ) :
225  m_linesize(scale.m_linesize) ,
226  m_image_dx(image_dx) ,
227  m_offset_x_3(offset_x*3U) ,
228  m_image_x(0U) ,
229  m_draw_bg(draw_bg) ,
230  m_fg(fg) ,
231  m_bg(bg)
232 {
233  G_ASSERT( offset_y < scale.m_dy ) ;
234  m_row = b.begin() + ( offset_y * m_linesize ) ;
235  m_p = m_row + m_offset_x_3 ;
236  m_lhs = m_p + ( lhs_blank * 3U ) ;
237  m_rhs = m_row + m_linesize ;
238 }
239 
240 void Gv::DemoImp::Out::clear( Gv::CaptureBuffer & b , triple_t bg )
241 {
242  const unsigned char * const end = b.end() ;
243  for( unsigned char * p = b.begin() ; p < end ; p += 3 )
244  {
245  p[0] = bg.r() ;
246  p[1] = bg.g() ;
247  p[2] = bg.b() ;
248  }
249 }
250 
251 void Gv::DemoImp::Out::operator()( bool b , size_t n )
252 {
253  if( m_draw_bg )
254  {
255  for( size_t i = 0U ; i < n && m_p < m_rhs ; i++ , m_p += 3 )
256  {
257  if( m_p > m_lhs )
258  {
259  m_p[0] = b ? m_fg.r() : m_bg.r() ;
260  m_p[1] = b ? m_fg.g() : m_bg.g() ;
261  m_p[2] = b ? m_fg.b() : m_bg.b() ;
262  }
263  }
264  }
265  else if( b )
266  {
267  for( size_t i = 0U ; i < n && m_p < m_rhs ; i++ , m_p += 3 )
268  {
269  if( m_p > m_lhs )
270  {
271  m_p[0] = m_fg.r() ;
272  m_p[1] = m_fg.g() ;
273  m_p[2] = m_fg.b() ;
274  }
275  }
276  }
277  else
278  {
279  m_p += n ; m_p += n ; m_p += n ;
280  if( m_p > m_rhs )
281  m_p = m_rhs ;
282  }
283 
284  m_image_x += n ; // (requires rle runs to break at rhs)
285  if( m_image_x >= m_image_dx )
286  {
287  m_image_x = 0U ;
288  m_lhs += m_linesize ;
289  m_rhs += m_linesize ;
290  m_row += m_linesize ;
291  m_p = m_row + m_offset_x_3 ;
292  }
293 }
294 
A subsecond-resolution timestamp based on a time_t.
Definition: gdatetime.h:39
virtual bool init() override
Override from Gv::ImageGenerator.
Definition: gvdemo.cpp:90
virtual ~Demo()
Destructor.
Definition: gvdemo.cpp:85
A representation of a timezone.
Definition: gvtimezone.h:35
const unsigned char * begin() const
Returns a pointer to start of the dword-aligned data buffer.
A simple time-of-day (hh/mm/ss) class.
Definition: gtime.h:38
const unsigned char * end() const
Returns a pointer off the end of the raw data buffer.
static EpochTime now()
Returns the current epoch time.
A video-capture buffer class to hold image data, with overloaded constructors for the various V4l i/o...
A pimple-pattern implementation class for Gv::Demo.
Definition: gvdemo.cpp:35
Demo(int dx, int dy, const std::string &dev_config, Gv::Timezone)
Constructor.
Definition: gvdemo.cpp:80
A structure holding capture buffer dimensions.
virtual void fillBuffer(CaptureBuffer &, const CaptureBufferScale &) override
Override from Gv::ImageGenerator.
Definition: gvdemo.cpp:95