base class for games
Stefan Schuermans

Stefan Schuermans commited on 2019-06-10 10:11:49
Showing 2 changed files, with 375 additions and 0 deletions.

... ...
@@ -0,0 +1,236 @@
1
+/* Blinker
2
+   Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
3
+   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4
+   a blinkenarea.org project */
5
+
6
+#include <string>
7
+#include <vector>
8
+
9
+#include <BlinkenLib/BlinkenFrame.h>
10
+
11
+#include "File.h"
12
+#include "Format.h"
13
+#include "FormatFile.h"
14
+#include "Game.h"
15
+#include "Mgrs.h"
16
+#include "Module.h"
17
+#include "OutStreamFile.h"
18
+
19
+namespace Blinker {
20
+
21
+/**
22
+ * @brief constructor
23
+ * @param[in] name module name
24
+ * @param[in] mgrs managers
25
+ * @param[in] dirBase base directory
26
+ */
27
+Game::Game(const std::string &name, Mgrs &mgrs, const Directory &dirBase):
28
+  Module(name, mgrs, dirBase),
29
+  m_fileFormat(dirBase.getFile("format")),
30
+  m_fileBackgroundColor(dirBase.getFile("backgroundColor")),
31
+  m_fileOutStream(dirBase.getFile("outstream"), mgrs.m_streamMgr),
32
+  m_height(0), m_width(0), m_channels(0), m_imgBuf(), m_backgroundColor()
33
+{
34
+}
35
+
36
+/// virtual destructor
37
+Game::~Game()
38
+{
39
+  // clean up
40
+  deactivate();
41
+}
42
+
43
+/// check for update of configuration
44
+void Game::updateConfig()
45
+{
46
+  // format file was modified -> re-create canvas and redraw
47
+  if (m_fileFormat.checkModified()) {
48
+    createImgBuf();
49
+  }
50
+
51
+  // color file was modified -> redraw image
52
+  if (m_fileBackgroundColor.checkModified()) {
53
+    redraw();
54
+  }
55
+
56
+  // output stream name file was modified -> re-get output stream
57
+  if (m_fileOutStream.checkModified()) {
58
+    m_fileOutStream.update();
59
+  }
60
+}
61
+
62
+/// activate game: set up image buffer, call redraw()
63
+void Game::activate()
64
+{
65
+  createImgBuf();
66
+  redraw();
67
+}
68
+
69
+/// deactivate game: tear down image buffer, deactivate output
70
+void Game::deactivate()
71
+{
72
+  destroyImgBuf();
73
+  sendFrame();
74
+}
75
+
76
+/// set pixel in image buffer
77
+void Game::pixel(int y, int x, ColorData const &cd)
78
+{
79
+  if (m_imgBuf.empty() ||
80
+      ! checkLimitInt(y, 0, m_height - 1) ||
81
+      ! checkLimitInt(x, 0, m_width - 1)) {
82
+    return;
83
+  }
84
+  int pos = (y * m_width + x) * m_channels;
85
+  std::copy(cd.begin(), cd.end(), m_imgBuf.begin() + pos);
86
+}
87
+
88
+/// draw horizontal line to image buffer
89
+void Game::lineHor(int y, int x1, int x2, ColorData const &cd)
90
+{
91
+  if (m_imgBuf.empty() ||
92
+      ! checkLimitInt(y, 0, m_height - 1) ||
93
+      ! checkIntRangeLimit(x1, x2, 0, m_width - 1)) {
94
+    return;
95
+  }
96
+  int pos = (y * m_width + x1) * m_channels;
97
+  int dx = m_channels;
98
+  for (int x = x1; x <= x2; ++x) {
99
+    std::copy(cd.begin(), cd.end(), m_imgBuf.begin() + pos);
100
+    pos += dx;
101
+  }
102
+}
103
+
104
+/// draw vertical line to image buffer
105
+void Game::lineVert(int y1, int y2, int x, ColorData const &cd)
106
+{
107
+  if (m_imgBuf.empty() ||
108
+      ! checkIntRangeLimit(y1, y2, 0, m_height - 1) ||
109
+      ! checkLimitInt(x, 0, m_width - 1)) {
110
+    return;
111
+  }
112
+  int pos = (y1 * m_width + x) * m_channels;
113
+  int dy = m_width * m_channels;
114
+  for (int y = y1; y <= y2; ++y) {
115
+    std::copy(cd.begin(), cd.end(), m_imgBuf.begin() + pos);
116
+    pos += dy;
117
+  }
118
+}
119
+
120
+/// draw non-filled rectangle to image buffer
121
+void Game::rect(int y1, int y2, int x1, int x2, ColorData const &cd)
122
+{
123
+  lineHor(y1, x1, x2, cd);
124
+  lineHor(y2, x1, x2, cd);
125
+  lineVert(y1, y2, x1, cd);
126
+  lineVert(y1, y2, x2, cd);
127
+}
128
+
129
+/// draw filled rectangle to image buffer
130
+void Game::rectFill(int y1, int y2, int x1, int x2, ColorData const &cd)
131
+{
132
+  if (m_imgBuf.empty() ||
133
+      ! checkIntRangeLimit(y1, y2, 0, m_height - 1) ||
134
+      ! checkIntRangeLimit(x1, x2, 0, m_width - 1)) {
135
+    return;
136
+  }
137
+  int pos = (y1 * m_width + x1) * m_channels;
138
+  int dx = m_channels;
139
+  int dy = m_width - (x2 - x1 + 1) * m_channels;
140
+  for (int y = y1; y <= y2; ++y) {
141
+    for (int x = x1; x <= x2; ++x) {
142
+      std::copy(cd.begin(), cd.end(), m_imgBuf.begin() + pos);
143
+      pos += dx;
144
+    }
145
+    pos += dy;
146
+  }
147
+}
148
+
149
+/// convert color to raw color data
150
+void Game::color2data(Format const &format, Color const &color,
151
+                      ColorData &data)
152
+{
153
+  data.resize(format.m_channels);
154
+  if (format.m_channels == 1) {
155
+    // single channel
156
+    // convert to monochrome according to CIE XYZ
157
+    double val = 0.2125 * color.m_red + 0.7154 * color.m_green
158
+                                      + 0.0721 * color.m_blue;
159
+    // adapt to maxval
160
+    val = val * format.m_maxval / 255.0;
161
+    // round
162
+    data.at(0) = (unsigned char)(val + 0.5);
163
+  } else if (format.m_channels == 2) {
164
+    // two channels
165
+    // adapt to maxval and round, ignore blue
166
+    data.at(0) = (unsigned char)(color.m_red * format.m_maxval / 255.0 + 0.5);
167
+    data.at(1) = (unsigned char)(color.m_green * format.m_maxval / 255.0 + 0.5);
168
+  } else if (format.m_channels >= 3) {
169
+    // three channels (more than three channels: further channels are dark)
170
+    // adapt to maxval and round
171
+    data.at(0) = (unsigned char)(color.m_red * format.m_maxval / 255.0 + 0.5);
172
+    data.at(1) = (unsigned char)(color.m_green * format.m_maxval / 255.0 + 0.5);
173
+    data.at(2) = (unsigned char)(color.m_blue * format.m_maxval / 255.0 + 0.5);
174
+  }
175
+}
176
+
177
+/// send current image buffer as frame to output stream
178
+void Game::sendFrame()
179
+{
180
+  // image buffer available -> send its contents as frame
181
+  if (! m_imgBuf.empty()) {
182
+    Format const &fmt = m_fileFormat.m_obj;
183
+    stBlinkenFrame *pFrame = BlinkenFrameNew(fmt.m_height, fmt.m_width,
184
+                                             fmt.m_channels, fmt.m_maxval, 1);
185
+    BlinkenFrameSetPixelData(pFrame, 0, m_height, 0, m_width, 0, m_channels,
186
+                             m_imgBuf.data());
187
+    m_fileOutStream.setFrame(pFrame);
188
+    BlinkenFrameFree(pFrame);
189
+  }
190
+  // no image buffer available -> send "no frame" information
191
+  else {
192
+    m_fileOutStream.setFrame(NULL);
193
+  }
194
+}
195
+
196
+/// (re-)create image buffer
197
+void Game::createImgBuf()
198
+{
199
+  // get rid of old image buffer
200
+  destroyImgBuf();
201
+
202
+  // read format from format file
203
+  m_fileFormat.update();
204
+  if (! m_fileFormat.m_valid)
205
+    return;
206
+
207
+  // read background color
208
+  m_fileBackgroundColor.update();
209
+  if (! m_fileBackgroundColor.m_valid)
210
+    return;
211
+
212
+  // store parameters
213
+  m_height = m_fileFormat.m_obj.m_height;
214
+  m_width = m_fileFormat.m_obj.m_width;
215
+  m_channels = m_fileFormat.m_obj.m_channels;
216
+
217
+  // create image buffer
218
+  m_imgBuf.resize(m_height * m_width * m_channels);
219
+
220
+  // convert background color
221
+  color2data(m_fileFormat.m_obj, m_fileBackgroundColor.m_obj,
222
+             m_backgroundColor);
223
+
224
+  // set image buffer to background color
225
+  rectFill(0, m_height, 0, m_width, m_backgroundColor);
226
+}
227
+
228
+/// tear down image buffer
229
+void Game::destroyImgBuf()
230
+{
231
+  m_imgBuf.clear();
232
+  m_backgroundColor.clear();
233
+}
234
+
235
+} // namespace Blinker
236
+
... ...
@@ -0,0 +1,139 @@
1
+/* Blinker
2
+   Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
3
+   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4
+   a blinkenarea.org project */
5
+
6
+#ifndef BLINKER_GAME_H
7
+#define BLINKER_GAME_H
8
+
9
+#include <string>
10
+#include <vector>
11
+
12
+#include <BlinkenLib/BlinkenFrame.h>
13
+
14
+#include "Color.h"
15
+#include "ColorFile.h"
16
+#include "File.h"
17
+#include "Format.h"
18
+#include "FormatFile.h"
19
+#include "Mgrs.h"
20
+#include "Module.h"
21
+#include "OutStreamFile.h"
22
+
23
+namespace Blinker {
24
+
25
+/// base class for games
26
+class Game: public Module
27
+{
28
+protected:
29
+  /// raw color data matching image buffer
30
+  typedef std::vector<unsigned char> ColorData;
31
+
32
+public:
33
+  /**
34
+   * @brief constructor
35
+   * @param[in] name module name
36
+   * @param[in] mgrs managers
37
+   * @param[in] dirBase base directory
38
+   */
39
+  Game(const std::string &name, Mgrs &mgrs, const Directory &dirBase);
40
+
41
+  /// virtual destructor
42
+  virtual ~Game();
43
+
44
+private:
45
+  /// copy constructor disabled
46
+  Game(const Game &that);
47
+
48
+  /// assignment operator disabled
49
+  const Game & operator=(const Game &that);
50
+
51
+public:
52
+  /// check for update of configuration
53
+  virtual void updateConfig();
54
+
55
+protected:
56
+  /// re-initialize game (e.g. due to config change)
57
+  virtual void reinitialize() = 0;
58
+
59
+  /// redraw current game image, expected to call sendFrame() at end
60
+  virtual void redraw() = 0;
61
+
62
+  /// activate game: set up image buffer, call redraw()
63
+  void activate();
64
+
65
+  /// deactivate game: tear down image buffer, deactivate output
66
+  void deactivate();
67
+
68
+  /// check if integer is between minimum and maximum values
69
+  static bool checkLimitInt(int i, int min, int max)
70
+  {
71
+    return i >= min && i <= max;
72
+  }
73
+
74
+  /// limit integer to minimum and maximum values
75
+  static void limitInt(int &i, int min, int max)
76
+  {
77
+    if (i < min) {
78
+      i = min;
79
+    }
80
+    if (i > max) {
81
+      i = max;
82
+    }
83
+  }
84
+
85
+  /// check if range of two integers is nonEmpty and limit the integers
86
+  static bool checkIntRangeLimit(int &i1, int &i2, int min, int max)
87
+  {
88
+    if (i1 > i2) {
89
+      return false;
90
+    }
91
+    limitInt(i1, min, max);
92
+    limitInt(i2, min, max);
93
+    return true;
94
+  }
95
+
96
+  /// set pixel in image buffer
97
+  void pixel(int y, int x, ColorData const &cd);
98
+
99
+  /// draw horizontal line to image buffer
100
+  void lineHor(int y, int x1, int x2, ColorData const &cd);
101
+
102
+  /// draw vertical line to image buffer
103
+  void lineVert(int y1, int y2, int x, ColorData const &cd);
104
+
105
+  /// draw non-filled rectangle to image buffer
106
+  void rect(int y1, int y2, int x1, int x2, ColorData const &cd);
107
+
108
+  /// draw filled rectangle to image buffer
109
+  void rectFill(int y1, int y2, int x1, int x2, ColorData const &cd);
110
+
111
+  /// convert color to raw color data
112
+  static void color2data(Format const &format, Color const &color,
113
+                         ColorData &data);
114
+
115
+  /// send current image buffer as frame to output stream
116
+  void sendFrame();
117
+
118
+private:
119
+  /// (re-)create image buffer
120
+  void createImgBuf();
121
+
122
+  /// tear down image buffer
123
+  void destroyImgBuf();
124
+
125
+protected:
126
+  FormatFile    m_fileFormat;          ///< format file for output
127
+  ColorFile     m_fileBackgroundColor; ///< color file for background color
128
+  OutStreamFile m_fileOutStream;       ///< output stream name file
129
+  int           m_height;              ///< height of image buffer
130
+  int           m_width;               ///< width of image buffer
131
+  int           m_channels;            ///< number of channels of image buffer
132
+  ColorData     m_imgBuf;              ///< image buffer (empty if none)
133
+  ColorData     m_backgroundColor;     ///< background color
134
+}; // class Canvas
135
+
136
+} // namespace Blinker
137
+
138
+#endif // #ifndef BLINKER_GAME_H
139
+
0 140