ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Bitmap.cpp
1 /*
2  * Copyright (C) 2012 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "Bitmap.h"
24 #include <WString.h>
25 #include <string.h>
26 #include <stdlib.h>
27 
88 Bitmap::Bitmap(int width, int height)
89  : _width(width)
90  , _height(height)
91  , _stride((width + 7) / 8)
92  , fb(0)
93  , _font(0)
94  , _textColor(White)
95 {
96  // Allocate memory for the framebuffer and clear it (1 = pixel off).
97  unsigned int size = _stride * _height;
98  fb = (uint8_t *)malloc(size);
99  if (fb)
100  memset(fb, 0xFF, size);
101 }
102 
107 {
108  if (fb)
109  free(fb);
110 }
111 
174 void Bitmap::clear(Color color)
175 {
176  unsigned int size = _stride * _height;
177  if (color == Black)
178  memset(fb, 0xFF, size);
179  else
180  memset(fb, 0x00, size);
181 }
182 
191 Bitmap::Color Bitmap::pixel(int x, int y) const
192 {
193  if (((unsigned int)x) >= ((unsigned int)_width) ||
194  ((unsigned int)y) >= ((unsigned int)_height))
195  return Black;
196  uint8_t *ptr = fb + y * _stride + (x >> 3);
197  if (*ptr & ((uint8_t)0x80) >> (x & 0x07))
198  return Black;
199  else
200  return White;
201 }
202 
208 void Bitmap::setPixel(int x, int y, Color color)
209 {
210  if (((unsigned int)x) >= ((unsigned int)_width) ||
211  ((unsigned int)y) >= ((unsigned int)_height))
212  return; // Pixel is off-screen.
213  uint8_t *ptr = fb + y * _stride + (x >> 3);
214  if (color)
215  *ptr &= ~(((uint8_t)0x80) >> (x & 0x07));
216  else
217  *ptr |= (((uint8_t)0x80) >> (x & 0x07));
218 }
219 
225 void Bitmap::drawLine(int x1, int y1, int x2, int y2, Color color)
226 {
227  // Midpoint line scan-conversion algorithm from "Computer Graphics:
228  // Principles and Practice", Second Edition, Foley, van Dam, et al.
229  int dx = x2 - x1;
230  int dy = y2 - y1;
231  int xstep, ystep;
232  int d, incrE, incrNE;
233  if (dx < 0) {
234  xstep = -1;
235  dx = -dx;
236  } else {
237  xstep = 1;
238  }
239  if (dy < 0) {
240  ystep = -1;
241  dy = -dy;
242  } else {
243  ystep = 1;
244  }
245  if (dx >= dy) {
246  d = 2 * dy - dx;
247  incrE = 2 * dy;
248  incrNE = 2 * (dy - dx);
249  setPixel(x1, y1, color);
250  while (x1 != x2) {
251  if (d <= 0) {
252  d += incrE;
253  } else {
254  d += incrNE;
255  y1 += ystep;
256  }
257  x1 += xstep;
258  setPixel(x1, y1, color);
259  }
260  } else {
261  d = 2 * dx - dy;
262  incrE = 2 * dx;
263  incrNE = 2 * (dx - dy);
264  setPixel(x1, y1, color);
265  while (y1 != y2) {
266  if (d <= 0) {
267  d += incrE;
268  } else {
269  d += incrNE;
270  x1 += xstep;
271  }
272  y1 += ystep;
273  setPixel(x1, y1, color);
274  }
275  }
276 }
277 
286 void Bitmap::drawRect(int x1, int y1, int x2, int y2, Color borderColor, Color fillColor)
287 {
288  int temp;
289  if (x1 > x2) {
290  temp = x1;
291  x1 = x2;
292  x2 = temp;
293  }
294  if (y1 > y2) {
295  temp = y1;
296  y1 = y2;
297  y2 = temp;
298  }
299  if (fillColor == borderColor) {
300  fill(x1, y1, x2 - x1 + 1, y2 - y1 + 1, fillColor);
301  } else {
302  drawLine(x1, y1, x2, y1, borderColor);
303  if (y1 < y2)
304  drawLine(x2, y1 + 1, x2, y2, borderColor);
305  if (x1 < x2)
306  drawLine(x2 - 1, y2, x1, y2, borderColor);
307  if (y1 < (y2 - 1))
308  drawLine(x1, y2 - 1, x1, y1 + 1, borderColor);
309  if (fillColor != NoFill)
310  fill(x1 + 1, y1 + 1, x2 - x1 - 1, y2 - y1 - 1, fillColor);
311  }
312 }
313 
334 void Bitmap::drawCircle(int centerX, int centerY, int radius, Color borderColor, Color fillColor)
335 {
336  // Midpoint circle scan-conversion algorithm using second-order
337  // differences from "Computer Graphics: Principles and Practice",
338  // Second Edition, Foley, van Dam, et al.
339  if (radius < 0)
340  radius = -radius;
341  int x = 0;
342  int y = radius;
343  int d = 1 - radius;
344  int deltaE = 3;
345  int deltaSE = 5 - 2 * radius;
346  drawCirclePoints(centerX, centerY, radius, x, y, borderColor, fillColor);
347  while (y > x) {
348  if (d < 0) {
349  d += deltaE;
350  deltaE += 2;
351  deltaSE += 2;
352  } else {
353  d += deltaSE;
354  deltaE += 2;
355  deltaSE += 4;
356  --y;
357  }
358  ++x;
359  drawCirclePoints(centerX, centerY, radius, x, y, borderColor, fillColor);
360  }
361 }
362 
388 void Bitmap::drawBitmap(int x, int y, const Bitmap &bitmap, Color color)
389 {
390  int w = bitmap.width();
391  int s = bitmap.stride();
392  int h = bitmap.height();
393  Color invColor = !color;
394  for (uint8_t by = 0; by < h; ++by) {
395  const uint8_t *line = bitmap.data() + by * s;
396  uint8_t mask = 0x80;
397  uint8_t value = *line++;
398  for (uint8_t bx = 0; bx < w; ++bx) {
399  if (value & mask)
400  setPixel(x + bx, y + by, invColor);
401  else
402  setPixel(x + bx, y + by, color);
403  mask >>= 1;
404  if (!mask) {
405  mask = 0x80;
406  value = *line++;
407  }
408  }
409  }
410 }
411 
425 void Bitmap::drawBitmap(int x, int y, Bitmap::ProgMem bitmap, Color color)
426 {
427  uint8_t w = pgm_read_byte(bitmap);
428  uint8_t s = (w + 7) >> 3;
429  uint8_t h = pgm_read_byte(bitmap + 1);
430  Color invColor = !color;
431  for (uint8_t by = 0; by < h; ++by) {
432  const uint8_t *line = ((const uint8_t *)bitmap) + 2 + by * s;
433  uint8_t mask = 0x80;
434  uint8_t value = pgm_read_byte(line);
435  for (uint8_t bx = 0; bx < w; ++bx) {
436  if (value & mask)
437  setPixel(x + bx, y + by, color);
438  else
439  setPixel(x + bx, y + by, invColor);
440  mask >>= 1;
441  if (!mask) {
442  mask = 0x80;
443  ++line;
444  value = pgm_read_byte(line);
445  }
446  }
447  }
448 }
449 
509 #define fontIsFixed(font) (pgm_read_byte((font)) == 0 && \
510  pgm_read_byte((font) + 1) == 0)
511 #define fontWidth(font) (pgm_read_byte((font) + 2))
512 #define fontHeight(font) (pgm_read_byte((font) + 3))
513 #define fontFirstChar(font) (pgm_read_byte((font) + 4))
514 #define fontCharCount(font) (pgm_read_byte((font) + 5))
515 
526 void Bitmap::drawText(int x, int y, const char *str, int len)
527 {
528  if (!_font)
529  return;
530  uint8_t height = fontHeight(_font);
531  if (len < 0)
532  len = strlen(str);
533  while (len-- > 0) {
534  x += drawChar(x, y, *str++);
535  if (len > 0) {
536  fill(x, y, 1, height, !_textColor);
537  ++x;
538  }
539  if (x >= _width)
540  break;
541  }
542 }
543 
555 void Bitmap::drawText(int x, int y, const String &str, int start, int len)
556 {
557  if (!_font)
558  return;
559  uint8_t height = fontHeight(_font);
560  if (len < 0)
561  len = str.length() - start;
562  while (len-- > 0) {
563  x += drawChar(x, y, str[start++]);
564  if (len > 0) {
565  fill(x, y, 1, height, !_textColor);
566  ++x;
567  }
568  if (x >= _width)
569  break;
570  }
571 }
572 
585 int Bitmap::drawChar(int x, int y, char ch)
586 {
587  uint8_t height = fontHeight(_font);
588  if (ch == ' ') {
589  // Font may not have space, or it is zero-width. Calculate
590  // the real size and fill the space.
591  int spaceWidth = charWidth('n');
592  fill(x, y, spaceWidth, height, !_textColor);
593  return spaceWidth;
594  }
595  uint8_t first = fontFirstChar(_font);
596  uint8_t count = fontCharCount(_font);
597  uint8_t index = (uint8_t)ch;
598  if (index < first || index >= (first + count))
599  return 0;
600  index -= first;
601  uint8_t heightBytes = (height + 7) >> 3;;
602  uint8_t width;
603  const uint8_t *image;
604  if (fontIsFixed(_font)) {
605  // Fixed-width font.
606  width = fontWidth(_font);
607  image = ((const uint8_t *)_font) + 6 + index * heightBytes * width;
608  } else {
609  // Variable-width font.
610  width = pgm_read_byte(_font + 6 + index);
611  image = ((const uint8_t *)_font) + 6 + count;
612  for (uint8_t temp = 0; temp < index; ++temp) {
613  // Scan through all previous characters to find the starting
614  // location for this one.
615  image += pgm_read_byte(_font + 6 + temp) * heightBytes;
616  }
617  }
618  if ((x + width) <= 0 || (y + height) <= 0)
619  return width; // Character is off the top or left of the screen.
620  Color invColor = !_textColor;
621  for (uint8_t cx = 0; cx < width; ++cx) {
622  for (uint8_t cy = 0; cy < heightBytes; ++cy) {
623  uint8_t value = pgm_read_byte(image + cy * width + cx);
624  int posn;
625  if (heightBytes > 1 && cy == (heightBytes - 1))
626  posn = height - 8;
627  else
628  posn = cy * 8;
629  for (uint8_t bit = 0; bit < 8; ++bit) {
630  if ((posn + bit) >= (cy * 8) && (posn + bit) <= height) {
631  if (value & 0x01)
632  setPixel(x + cx, y + posn + bit, _textColor);
633  else
634  setPixel(x + cx, y + posn + bit, invColor);
635  }
636  value >>= 1;
637  }
638  }
639  }
640  return width;
641 }
642 
650 int Bitmap::charWidth(char ch) const
651 {
652  uint8_t index = (uint8_t)ch;
653  if (!_font)
654  return 0;
655  uint8_t first = fontFirstChar(_font);
656  uint8_t count = fontCharCount(_font);
657  if (index == ' ')
658  index = 'n'; // In case the font does not contain space.
659  if (index < first || index >= (first + count))
660  return 0;
661  if (fontIsFixed(_font))
662  return fontWidth(_font);
663  else
664  return pgm_read_byte(_font + 6 + (index - first));
665 }
666 
675 int Bitmap::textWidth(const char *str, int len) const
676 {
677  int width = 0;
678  if (len < 0)
679  len = strlen(str);
680  while (len-- > 0) {
681  width += charWidth(*str++);
682  if (len > 0)
683  ++width;
684  }
685  return width;
686 }
687 
697 int Bitmap::textWidth(const String &str, int start, int len) const
698 {
699  int width = 0;
700  if (len < 0)
701  len = str.length() - start;
702  while (len-- > 0) {
703  width += charWidth(str[start++]);
704  if (len > 0)
705  ++width;
706  }
707  return width;
708 }
709 
717 {
718  if (_font)
719  return fontHeight(_font);
720  else
721  return 0;
722 }
723 
738 void Bitmap::copy(int x, int y, int width, int height, Bitmap *dest, int destX, int destY)
739 {
740  if (dest == this) {
741  // Copying to within the same bitmap, so copy in a direction
742  // that will prevent problems with overlap.
743  blit(x, y, x + width - 1, y + height - 1, destX, destY);
744  } else {
745  // Copying to a different bitmap.
746  while (height > 0) {
747  for (int tempx = 0; tempx < width; ++tempx)
748  dest->setPixel(destX + tempx, destY, pixel(x + tempx, y));
749  ++y;
750  ++destY;
751  --height;
752  }
753  }
754 }
755 
762 void Bitmap::fill(int x, int y, int width, int height, Color color)
763 {
764  while (height > 0) {
765  for (int temp = 0; temp < width; ++temp)
766  setPixel(x + temp, y, color);
767  ++y;
768  --height;
769  }
770 }
771 
785 void Bitmap::fill(int x, int y, int width, int height, Bitmap::ProgMem pattern, Color color)
786 {
787  uint8_t w = pgm_read_byte(pattern);
788  uint8_t s = (w + 7) >> 3;
789  uint8_t h = pgm_read_byte(pattern + 1);
790  if (!w || !h)
791  return;
792  Color invColor = !color;
793  for (int tempy = 0; tempy < height; ++tempy) {
794  const uint8_t *startLine = ((const uint8_t *)pattern) + 2 + (tempy % h) * s;
795  const uint8_t *line = startLine;
796  uint8_t mask = 0x80;
797  uint8_t value = pgm_read_byte(line++);
798  int bit = 0;
799  for (int tempx = 0; tempx < width; ++tempx) {
800  if (value & mask)
801  setPixel(x + tempx, y + tempy, color);
802  else
803  setPixel(x + tempx, y + tempy, invColor);
804  if (++bit >= w) {
805  mask = 0x80;
806  line = startLine;
807  value = pgm_read_byte(line++);
808  bit = 0;
809  } else {
810  mask >>= 1;
811  if (!mask) {
812  mask = 0x80;
813  value = pgm_read_byte(line++);
814  }
815  }
816  }
817  }
818 }
819 
841 void Bitmap::scroll(int x, int y, int width, int height, int dx, int dy, Color fillColor)
842 {
843  // Bail out if no scrolling at all.
844  if (!dx && !dy)
845  return;
846 
847  // Clamp the scroll region to the extents of the bitmap.
848  if (x < 0) {
849  width += x;
850  x = 0;
851  }
852  if (y < 0) {
853  height += y;
854  y = 0;
855  }
856  if ((x + width) > _width)
857  width = _width - x;
858  if ((y + height) > _height)
859  height = _height - y;
860  if (width <= 0 || height <= 0)
861  return;
862 
863  // Scroll the region in the specified direction.
864  if (dy < 0) {
865  if (dx < 0)
866  blit(x - dx, y - dy, x + width - 1 + dx, y + height - 1 + dy, x, y);
867  else
868  blit(x, y - dy, x + width - 1 - dx, y + height - 1 + dy, x + dx, y);
869  } else {
870  if (dx < 0)
871  blit(x - dx, y, x + width - 1 + dx, y + height - 1 - dy, x, y + dy);
872  else
873  blit(x, y, x + width - 1 - dx, y + height - 1 - dy, x + dx, y + dy);
874  }
875 
876  // Fill the pixels that were uncovered by the scroll.
877  if (dy < 0) {
878  fill(x, y + height + dy, width, -dy, fillColor);
879  if (dx < 0)
880  fill(x + width + dx, y, -dx, height + dy, fillColor);
881  else if (dx > 0)
882  fill(x, y, dx, height + dy, fillColor);
883  } else if (dy > 0) {
884  fill(x, y, width, -dy, fillColor);
885  if (dx < 0)
886  fill(x + width + dx, y + dy, -dx, height - dy, fillColor);
887  else if (dx > 0)
888  fill(x, y + dy, dx, height - dy, fillColor);
889  } else if (dx < 0) {
890  fill(x + width + dx, y, -dx, height, fillColor);
891  } else if (dx > 0) {
892  fill(x, y, dx, height, fillColor);
893  }
894 }
895 
902 void Bitmap::invert(int x, int y, int width, int height)
903 {
904  while (height > 0) {
905  for (int tempx = x + width - 1; tempx >= x; --tempx)
906  setPixel(tempx, y, !pixel(tempx, y));
907  --height;
908  ++y;
909  }
910 }
911 
912 void Bitmap::blit(int x1, int y1, int x2, int y2, int x3, int y3)
913 {
914  if (y3 < y1 || (y1 == y3 && x3 <= x1)) {
915  for (int tempy = y1; tempy <= y2; ++tempy) {
916  int y = y1 - tempy + y3;
917  int x = x3 - x1;
918  for (int tempx = x1; tempx <= x2; ++tempx)
919  setPixel(x + tempx, y, pixel(tempx, tempy));
920  }
921  } else {
922  for (int tempy = y2; tempy >= y1; --tempy) {
923  int y = y1 - tempy + y3;
924  int x = x3 - x1;
925  for (int tempx = x2; tempx >= x1; --tempx)
926  setPixel(x + tempx, y, pixel(tempx, tempy));
927  }
928  }
929 }
930 
931 void Bitmap::drawCirclePoints(int centerX, int centerY, int radius, int x, int y, Color borderColor, Color fillColor)
932 {
933  if (x != y) {
934  setPixel(centerX + x, centerY + y, borderColor);
935  setPixel(centerX + y, centerY + x, borderColor);
936  setPixel(centerX + y, centerY - x, borderColor);
937  setPixel(centerX + x, centerY - y, borderColor);
938  setPixel(centerX - x, centerY - y, borderColor);
939  setPixel(centerX - y, centerY - x, borderColor);
940  setPixel(centerX - y, centerY + x, borderColor);
941  setPixel(centerX - x, centerY + y, borderColor);
942  if (fillColor != NoFill) {
943  if (radius > 1) {
944  drawLine(centerX - x + 1, centerY + y, centerX + x - 1, centerY + y, fillColor);
945  drawLine(centerX - y + 1, centerY + x, centerX + y - 1, centerY + x, fillColor);
946  drawLine(centerX - x + 1, centerY - y, centerX + x - 1, centerY - y, fillColor);
947  drawLine(centerX - y + 1, centerY - x, centerX + y - 1, centerY - x, fillColor);
948  } else if (radius == 1) {
949  setPixel(centerX, centerY, fillColor);
950  }
951  }
952  } else {
953  setPixel(centerX + x, centerY + y, borderColor);
954  setPixel(centerX + y, centerY - x, borderColor);
955  setPixel(centerX - x, centerY - y, borderColor);
956  setPixel(centerX - y, centerY + x, borderColor);
957  if (fillColor != NoFill) {
958  if (radius > 1) {
959  drawLine(centerX - x + 1, centerY + y, centerX + x - 1, centerY + y, fillColor);
960  drawLine(centerX - x + 1, centerY - y, centerX + x - 1, centerY - y, fillColor);
961  } else if (radius == 1) {
962  setPixel(centerX, centerY, fillColor);
963  }
964  }
965  }
966 }
int width() const
Returns the width of the bitmap in pixels.
Definition: Bitmap.h:48
void copy(int x, int y, int width, int height, Bitmap *dest, int destX, int destY)
Copies the width x height pixels starting at top-left corner (x, y) to (destX, destY) in the bitmap d...
Definition: Bitmap.cpp:738
void scroll(int dx, int dy, Color fillColor=Black)
Scrolls the entire contents of the bitmap by dx and dy.
Definition: Bitmap.h:135
Represents a monochrome bitmap within main memory.
Definition: Bitmap.h:32
void drawRect(int x1, int y1, int x2, int y2, Color borderColor=White, Color fillColor=NoFill)
Draws a rectangle from (x1, y1) to (x2, y2), with the outline in borderColor and the interior filled ...
Definition: Bitmap.cpp:286
void setPixel(int x, int y, Color color)
Sets the pixel at (x, y) to color.
Definition: Bitmap.cpp:208
void drawLine(int x1, int y1, int x2, int y2, Color color=White)
Draws a line from (x1, y1) to (x2, y2) in color.
Definition: Bitmap.cpp:225
void drawBitmap(int x, int y, const Bitmap &bitmap, Color color=White)
Draws bitmap at (x, y) in color.
Definition: Bitmap.cpp:388
void drawCircle(int centerX, int centerY, int radius, Color borderColor=White, Color fillColor=NoFill)
Draws a circle with a specific center (centerX, centerY) and radius, with the outline in borderColor ...
Definition: Bitmap.cpp:334
int drawChar(int x, int y, char ch)
Draws a single character ch at (x, y).
Definition: Bitmap.cpp:585
PGM_VOID_P ProgMem
Type that represents a bitmap within program memory.
Definition: Bitmap.h:41
uint8_t Color
Type that represents the color of a pixel in a bitmap.
Definition: Bitmap.h:40
int height() const
Returns the height of the bitmap in pixels.
Definition: Bitmap.h:49
static const Color NoFill
Special color value that is used with drawRect() and drawCircle() to indicate that the interior of th...
Definition: Bitmap.h:46
int textWidth(const char *str, int len=-1) const
Returns the width in pixels of the len characters of str in the current font(), including inter-chara...
Definition: Bitmap.cpp:675
void fill(int x, int y, int width, int height, Color color)
Fills the width x height pixels starting at top-left corner (x, y) with color.
Definition: Bitmap.cpp:762
uint8_t * data()
Returns a pointer to the start of the bitmap's data buffer.
Definition: Bitmap.h:53
int textHeight() const
Returns the height in pixels of the current text drawing font(); or zero if font() is not set...
Definition: Bitmap.cpp:716
int charWidth(char ch) const
Returns the width in pixels of ch in the current font().
Definition: Bitmap.cpp:650
Bitmap(int width, int height)
Constructs a new in-memory bitmap that is width x height pixels in size.
Definition: Bitmap.cpp:88
static const Color White
Color value corresponding to "white". If the bitmap is displayed on a LED array, then it may have a d...
Definition: Bitmap.h:45
int stride() const
Returns the number of bytes in each line of the bitmap's data() buffer.
Definition: Bitmap.h:50
static const Color Black
Color value corresponding to "black".
Definition: Bitmap.h:44
void clear(Color color=Black)
Clears the entire bitmap to the specified color.
Definition: Bitmap.cpp:174
void invert(int x, int y, int width, int height)
Inverts the width x height pixels starting at top-left corner (x, y).
Definition: Bitmap.cpp:902
void drawText(int x, int y, const char *str, int len=-1)
Draws the len characters of str at (x, y).
Definition: Bitmap.cpp:526
~Bitmap()
Destroys this bitmap.
Definition: Bitmap.cpp:106
Color pixel(int x, int y) const
Returns the color of the pixel at (x, y); either Black or White.
Definition: Bitmap.cpp:191