OmEspHelpers
OmBmp.h
1 #include <stdint.h>
2 #include "OmXmlWriter.h"
3 
4 // This has classes for writing BMPs directly into the OmXmlWriter.
5 // So you could serve a bitmap image, or embed it right in the <img> html element
6 // as a data attribute.
7 
12 #ifndef UNUSED
13 #define UNUSED(x) (void)(x)
14 #endif
15 
18 {
19 public:
20  OmIByteStream *consumer;
21  bool put(uint8_t ch) override
22  {
23  UNUSED(ch);
24  return false; // not supported by this streamer
25  }
26 
27  int width;
28  int height;
29  bool didPutHeader;
30  int x;
31  int y;
32  int rowPadBytes;
33  uint8_t byteInProgress;
34 
35  bool putBmpHeader(int width, int height)
36  {
37  int rowBytes = ((width + 7) / 8 + 3) & 0xfffffffc;
38  int dataSize = rowBytes * height;
39  int paletteSize = 8; // 2 colors, 4 bytes each.
40  const int bmpHeaderSize = 14;
41  const int bmpInfoHeaderSize = 40;
42  int fileSize = bmpHeaderSize + bmpInfoHeaderSize + paletteSize + dataSize;
43 
44  bool result = true;
45  // header
46  result &= put8('B');
47  result &= put8('M');
48  result &= put32(fileSize);
49  result &= put16(0); // reserved1
50  result &= put16(0); // reserved2
51  result &= put32(bmpHeaderSize + bmpInfoHeaderSize + paletteSize); // offset to data
52 
53  // DIB info header
54  result &= put32(bmpInfoHeaderSize);
55  result &= put32(this->width); // width
56  result &= put32(this->height); // height
57  result &= put16(1); // planes
58  result &= put16(1); // bits
59  result &= put32(0); // compression
60  result &= put32(dataSize);
61  result &= put32(1000); // xresolution px/m
62  result &= put32(1000); // yresolution
63  result &= put32(2); // ncolours
64  result &= put32(0); // important colors
65 
66  // Write the two color palette.
67 
68  result &= put32(0x00101010); // color black. (not really black and white, for my thing this looks good)
69  result &= put32(0x00c0c0c0); // color white.
70  return result;
71  }
72 
74  OmBitmap1BmpStream(OmIByteStream *consumer, int width, int height)
75  {
76  this->consumer = consumer;
77  this->didPutHeader = false;
78  this->x = 0;
79  this->y = 0;
80  this->width = width;
81  this->height = height;
82  this->byteInProgress = 0;
83 
86  this->rowPadBytes = (4 - ((width % 32) + 7) / 8) & 3; // bytes to emit BEFORE hitting 8 pixels, when x == width
87  }
88 
92  bool put1Bit(int bit)
93  {
94  if(this->x >= this->width && this->y >= this->height)
95  return false;
96  bool result = true;
97 
98  if(!didPutHeader)
99  {
100  result &= this->putBmpHeader(this->width, this->height);
101  this->didPutHeader = true;
102  }
103  this->byteInProgress |= bit << (7 - this->x % 8);
104  this->x++;
105  if(this->x == this->width || (this->x & 0x0007) == 0) // finished one byte in progress
106  {
107  this->consumer->put(this->byteInProgress);
108  this->byteInProgress = 0;
109  }
110 
111  if(this->x == this->width)
112  {
113  // at the end of the row, do our special dance
114  for(int ix = 0; ix < this->rowPadBytes; ix++)
115  result &= this->put8(0);
116  this->x = 0;
117  this->y++;
118  }
119  return result;
120  }
121 
122 
123 private:
124  bool putN(int n, uint8_t *data)
125  {
126  bool result = true;
127  while(n-- > 0)
128  result &= this->consumer->put(*data++);
129  return result;
130  }
131 
132  bool put8(uint8_t x)
133  {
134  return this->putN(1, &x);
135  }
136 
137  bool put16(uint16_t x)
138  {
139  return this->putN(2, (uint8_t *)&x);
140  }
141 
142  bool put32(uint32_t x)
143  {
144  return this->putN(4, (uint8_t *)&x);
145  }
146 
147 };
148 
149 
152 {
153 public:
154  OmIByteStream *consumer;
155  bool didPutHeader = false;
156  int width;
157  int height;
158  int x;
159  int y;
160 
161  OmBitmap24BmpStream(OmIByteStream *consumer, int width, int height)
162  {
163  this->consumer = consumer;
164  this->width = width;
165  this->height = height;
166  this->didPutHeader = false;
167  this->x = 0;
168  this->y = 0; // position in progress writing
169  }
170 
171  bool put(uint8_t ch) override
172  {
173  UNUSED(ch);
174  return false; // not supported by this streamer
175  }
176 
177  bool putBmpHeader()
178  {
179  //ok, go!
180  int rowBytes = (this->width * 3 + 3) & 0xffffFFFC;
181  int dataSize = rowBytes * this->height;
182  int paletteSize = 0; // no color palette
183  const int bmpHeaderSize = 14;
184  const int bmpInfoHeaderSize = 40;
185  int fileSize = bmpHeaderSize + bmpInfoHeaderSize + paletteSize + dataSize;
186 
187  bool result = true;
188  // header
189  result &= put8('B');
190  result &= put8('M');
191  result &= put32(fileSize);
192  result &= put16(0); // reserved1
193  result &= put16(0); // reserved2
194  result &= put32(bmpHeaderSize + bmpInfoHeaderSize + paletteSize); // offset to data
195 
196  // DIB info header
197  result &= put32(bmpInfoHeaderSize);
198  result &= put32(this->width); // width
199  result &= put32(this->height); // height
200  result &= put16(1); // planes
201  result &= put16(24); // bits/px
202  result &= put32(0); // compression
203  result &= put32(dataSize);
204  result &= put32(1000); // xresolution px/m
205  result &= put32(1000); // yresolution
206  result &= put32(2); // ncolours
207  result &= put32(0); // important colors
208 
209  // And no color palette.
210  return result;
211  }
212 
216  bool put1Pixel(uint8_t r, uint8_t g, uint8_t b)
217  {
218  if(this->x >= this->width && this->y >= this->height)
219  return false;
220  bool result = true;
221 
222  if(!didPutHeader)
223  {
224  result &= this->putBmpHeader();
225  this->didPutHeader = true;
226  }
227  result &= this->put8(b);
228  result &= this->put8(g);
229  result &= this->put8(r);
230 
231  this->x++;
232  if(this->x >= this->width)
233  {
234  // Each row must be padded to a multiple of four BYTES.
235  // since each pixel is 3 bytes, this can happen.
236  int extras = (4 - (this->width * 3) % 4) % 4;
237  while(extras-- > 0)
238  this->put8(0);
239 
240  this->x = 0;
241  this->y++;
242  // And did we just hit the final row?
243  if(this->y >= this->height)
244  {
245  this->consumer->done();
246  this->done();
247  }
248  }
249 
250  return result;
251  }
252 
253 private:
254  bool putN(int n, uint8_t *data)
255  {
256  bool result = true;
257  while(n-- > 0)
258  result &= this->consumer->put(*data++);
259  return result;
260  }
261 
262  bool put8(uint8_t x)
263  {
264  return this->putN(1, &x);
265  }
266 
267  bool put16(uint16_t x)
268  {
269  return this->putN(2, (uint8_t *)&x);
270  }
271 
272  bool put32(uint32_t x)
273  {
274  return this->putN(4, (uint8_t *)&x);
275  }
276 
277 };
278 
279 extern const uint8_t base64_table[65];
280 
281 
282 
287 {
288 public:
289  OmIByteStream *consumer = NULL;
290 
291  OmBase64Stream(OmIByteStream *consumer)
292  {
293  this->consumer = consumer;
294  }
295 
296  uint8_t buf[3];
297  int bufSize = 0;
298  bool put(uint8_t ch) override
299  {
300  if(this->isDone)
301  return false;
302 
303  bool result = true;
304  if(this->bufSize < 3)
305  this->buf[this->bufSize++] = ch;
306 
307  if(this->bufSize == 3)
308  {
309  // when we get three chars, emit four.
310  result &= this->consumer->put(base64_table[this->buf[0] >> 2]);
311  result &= this->consumer->put(base64_table[((this->buf[0] & 0x03) << 4) | (this->buf[1] >> 4)]);
312  result &= this->consumer->put(base64_table[((this->buf[1] & 0x0f) << 2) | (this->buf[2] >> 6)]);
313  result &= this->consumer->put(base64_table[this->buf[2] & 0x3f]);
314  bufSize = 0;
315  }
316  return result;
317  }
318 
319  bool done() override
320  {
321  bool result = true;
322  this->isDone = true;
323  // close any last bits
324  switch(this->bufSize)
325  {
326  case 0:
327  break;
328 
329  case 1:
330  result &= this->consumer->put(base64_table[this->buf[0] >> 2]);
331  result &= this->consumer->put(base64_table[(this->buf[0] & 0x03) << 4]);
332  result &= this->consumer->put('=');
333  result &= this->consumer->put('=');
334  break;
335 
336  case 2:
337  result &= this->consumer->put(base64_table[this->buf[0] >> 2]);
338  result &= this->consumer->put(base64_table[((this->buf[0] & 0x03) << 4) | (this->buf[1] >> 4)]);
339  result &= this->consumer->put(base64_table[(this->buf[1] & 0x0f) << 2]);
340  result &= this->consumer->put('=');
341  break;
342  }
343  this->bufSize = 0;
344  return result;
345  }
346 };
OmBitmap1BmpStream::put
bool put(uint8_t ch) override
emit a single byte, overridden by any implementation
Definition: OmBmp.h:21
OmIByteStream::put
virtual bool put(uint8_t ch)
emit a single byte, overridden by any implementation
Definition: OmXmlWriter.h:58
OmBitmap24BmpStream::put1Pixel
bool put1Pixel(uint8_t r, uint8_t g, uint8_t b)
emit a single 24-bit pixel of our pixel map image. Will return true for each pixel you send,...
Definition: OmBmp.h:216
OmBase64Stream::put
bool put(uint8_t ch) override
emit a single byte, overridden by any implementation
Definition: OmBmp.h:298
OmBitmap24BmpStream
class to stream a 24-bit .bmp pixel map file, as you feed it pixel by pixel
Definition: OmBmp.h:152
OmIByteStream
Definition: OmXmlWriter.h:53
OmBase64Stream
Given a consumer, feed in bytes and put out base64 bytes. call done() to emit the last bit of padding...
Definition: OmBmp.h:287
OmBitmap1BmpStream
class to stream a 1-bit .bmp bitmap file, as you feed it pixel by pixel
Definition: OmBmp.h:18
OmBitmap1BmpStream::OmBitmap1BmpStream
OmBitmap1BmpStream(OmIByteStream *consumer, int width, int height)
Instantiate the bitmap streamer, with width and height.
Definition: OmBmp.h:74
OmBitmap24BmpStream::put
bool put(uint8_t ch) override
emit a single byte, overridden by any implementation
Definition: OmBmp.h:171
OmBitmap1BmpStream::put1Bit
bool put1Bit(int bit)
emit a single 1-bit pixel of our bitmap image. Will return true for each bit you send,...
Definition: OmBmp.h:92