ArduinoLibs
|
00001 /* 00002 * Copyright (C) 2012 Southern Storm Software, Pty Ltd. 00003 * 00004 * Permission is hereby granted, free of charge, to any person obtaining a 00005 * copy of this software and associated documentation files (the "Software"), 00006 * to deal in the Software without restriction, including without limitation 00007 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00008 * and/or sell copies of the Software, and to permit persons to whom the 00009 * Software is furnished to do so, subject to the following conditions: 00010 * 00011 * The above copyright notice and this permission notice shall be included 00012 * in all copies or substantial portions of the Software. 00013 * 00014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00015 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00019 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00020 * DEALINGS IN THE SOFTWARE. 00021 */ 00022 00023 #include "Bitmap.h" 00024 #include <WString.h> 00025 #include <string.h> 00026 #include <stdlib.h> 00027 00078 Bitmap::Bitmap(int width, int height) 00079 : _width(width) 00080 , _height(height) 00081 , _stride((width + 7) / 8) 00082 , fb(0) 00083 , _font(0) 00084 , _textColor(White) 00085 { 00086 // Allocate memory for the framebuffer and clear it (1 = pixel off). 00087 unsigned int size = _stride * _height; 00088 fb = (uint8_t *)malloc(size); 00089 if (fb) 00090 memset(fb, 0xFF, size); 00091 } 00092 00096 Bitmap::~Bitmap() 00097 { 00098 if (fb) 00099 free(fb); 00100 } 00101 00164 void Bitmap::clear(Color color) 00165 { 00166 unsigned int size = _stride * _height; 00167 if (color == Black) 00168 memset(fb, 0xFF, size); 00169 else 00170 memset(fb, 0x00, size); 00171 } 00172 00181 Bitmap::Color Bitmap::pixel(int x, int y) const 00182 { 00183 if (((unsigned int)x) >= ((unsigned int)_width) || 00184 ((unsigned int)y) >= ((unsigned int)_height)) 00185 return Black; 00186 uint8_t *ptr = fb + y * _stride + (x >> 3); 00187 if (*ptr & ((uint8_t)0x80) >> (x & 0x07)) 00188 return Black; 00189 else 00190 return White; 00191 } 00192 00198 void Bitmap::setPixel(int x, int y, Color color) 00199 { 00200 if (((unsigned int)x) >= ((unsigned int)_width) || 00201 ((unsigned int)y) >= ((unsigned int)_height)) 00202 return; // Pixel is off-screen. 00203 uint8_t *ptr = fb + y * _stride + (x >> 3); 00204 if (color) 00205 *ptr &= ~(((uint8_t)0x80) >> (x & 0x07)); 00206 else 00207 *ptr |= (((uint8_t)0x80) >> (x & 0x07)); 00208 } 00209 00215 void Bitmap::drawLine(int x1, int y1, int x2, int y2, Color color) 00216 { 00217 // Midpoint line scan-conversion algorithm from "Computer Graphics: 00218 // Principles and Practice", Second Edition, Foley, van Dam, et al. 00219 int dx = x2 - x1; 00220 int dy = y2 - y1; 00221 int xstep, ystep; 00222 int d, incrE, incrNE; 00223 if (dx < 0) { 00224 xstep = -1; 00225 dx = -dx; 00226 } else { 00227 xstep = 1; 00228 } 00229 if (dy < 0) { 00230 ystep = -1; 00231 dy = -dy; 00232 } else { 00233 ystep = 1; 00234 } 00235 if (dx >= dy) { 00236 d = 2 * dy - dx; 00237 incrE = 2 * dy; 00238 incrNE = 2 * (dy - dx); 00239 setPixel(x1, y1, color); 00240 while (x1 != x2) { 00241 if (d <= 0) { 00242 d += incrE; 00243 } else { 00244 d += incrNE; 00245 y1 += ystep; 00246 } 00247 x1 += xstep; 00248 setPixel(x1, y1, color); 00249 } 00250 } else { 00251 d = 2 * dx - dy; 00252 incrE = 2 * dx; 00253 incrNE = 2 * (dx - dy); 00254 setPixel(x1, y1, color); 00255 while (y1 != y2) { 00256 if (d <= 0) { 00257 d += incrE; 00258 } else { 00259 d += incrNE; 00260 x1 += xstep; 00261 } 00262 y1 += ystep; 00263 setPixel(x1, y1, color); 00264 } 00265 } 00266 } 00267 00276 void Bitmap::drawRect(int x1, int y1, int x2, int y2, Color borderColor, Color fillColor) 00277 { 00278 int temp; 00279 if (x1 > x2) { 00280 temp = x1; 00281 x1 = x2; 00282 x2 = temp; 00283 } 00284 if (y1 > y2) { 00285 temp = y1; 00286 y1 = y2; 00287 y2 = temp; 00288 } 00289 if (fillColor == borderColor) { 00290 fill(x1, y1, x2 - x1 + 1, y2 - y1 + 1, fillColor); 00291 } else { 00292 drawLine(x1, y1, x2, y1, borderColor); 00293 if (y1 < y2) 00294 drawLine(x2, y1 + 1, x2, y2, borderColor); 00295 if (x1 < x2) 00296 drawLine(x2 - 1, y2, x1, y2, borderColor); 00297 if (y1 < (y2 - 1)) 00298 drawLine(x1, y2 - 1, x1, y1 + 1, borderColor); 00299 if (fillColor != NoFill) 00300 fill(x1 + 1, y1 + 1, x2 - x1 - 1, y2 - y1 - 1, fillColor); 00301 } 00302 } 00303 00324 void Bitmap::drawCircle(int centerX, int centerY, int radius, Color borderColor, Color fillColor) 00325 { 00326 // Midpoint circle scan-conversion algorithm using second-order 00327 // differences from "Computer Graphics: Principles and Practice", 00328 // Second Edition, Foley, van Dam, et al. 00329 if (radius < 0) 00330 radius = -radius; 00331 int x = 0; 00332 int y = radius; 00333 int d = 1 - radius; 00334 int deltaE = 3; 00335 int deltaSE = 5 - 2 * radius; 00336 drawCirclePoints(centerX, centerY, radius, x, y, borderColor, fillColor); 00337 while (y > x) { 00338 if (d < 0) { 00339 d += deltaE; 00340 deltaE += 2; 00341 deltaSE += 2; 00342 } else { 00343 d += deltaSE; 00344 deltaE += 2; 00345 deltaSE += 4; 00346 --y; 00347 } 00348 ++x; 00349 drawCirclePoints(centerX, centerY, radius, x, y, borderColor, fillColor); 00350 } 00351 } 00352 00378 void Bitmap::drawBitmap(int x, int y, const Bitmap &bitmap, Color color) 00379 { 00380 int w = bitmap.width(); 00381 int s = bitmap.stride(); 00382 int h = bitmap.height(); 00383 Color invColor = !color; 00384 for (uint8_t by = 0; by < h; ++by) { 00385 const uint8_t *line = bitmap.data() + by * s; 00386 uint8_t mask = 0x80; 00387 uint8_t value = *line++; 00388 for (uint8_t bx = 0; bx < w; ++bx) { 00389 if (value & mask) 00390 setPixel(x + bx, y + by, invColor); 00391 else 00392 setPixel(x + bx, y + by, color); 00393 mask >>= 1; 00394 if (!mask) { 00395 mask = 0x80; 00396 value = *line++; 00397 } 00398 } 00399 } 00400 } 00401 00415 void Bitmap::drawBitmap(int x, int y, const prog_uint8_t *bitmap, Color color) 00416 { 00417 uint8_t w = pgm_read_byte(bitmap); 00418 uint8_t s = (w + 7) >> 3; 00419 uint8_t h = pgm_read_byte(bitmap + 1); 00420 Color invColor = !color; 00421 for (uint8_t by = 0; by < h; ++by) { 00422 const prog_uint8_t *line = bitmap + 2 + by * s; 00423 uint8_t mask = 0x80; 00424 uint8_t value = pgm_read_byte(line); 00425 for (uint8_t bx = 0; bx < w; ++bx) { 00426 if (value & mask) 00427 setPixel(x + bx, y + by, color); 00428 else 00429 setPixel(x + bx, y + by, invColor); 00430 mask >>= 1; 00431 if (!mask) { 00432 mask = 0x80; 00433 ++line; 00434 value = pgm_read_byte(line); 00435 } 00436 } 00437 } 00438 } 00439 00499 #define fontIsFixed(font) (pgm_read_byte((font)) == 0 && \ 00500 pgm_read_byte((font) + 1) == 0) 00501 #define fontWidth(font) (pgm_read_byte((font) + 2)) 00502 #define fontHeight(font) (pgm_read_byte((font) + 3)) 00503 #define fontFirstChar(font) (pgm_read_byte((font) + 4)) 00504 #define fontCharCount(font) (pgm_read_byte((font) + 5)) 00505 00516 void Bitmap::drawText(int x, int y, const char *str, int len) 00517 { 00518 if (!_font) 00519 return; 00520 uint8_t height = fontHeight(_font); 00521 if (len < 0) 00522 len = strlen(str); 00523 while (len-- > 0) { 00524 x += drawChar(x, y, *str++); 00525 if (len > 0) { 00526 fill(x, y, 1, height, !_textColor); 00527 ++x; 00528 } 00529 if (x >= _width) 00530 break; 00531 } 00532 } 00533 00545 void Bitmap::drawText(int x, int y, const String &str, int start, int len) 00546 { 00547 if (!_font) 00548 return; 00549 uint8_t height = fontHeight(_font); 00550 if (len < 0) 00551 len = str.length() - start; 00552 while (len-- > 0) { 00553 x += drawChar(x, y, str[start++]); 00554 if (len > 0) { 00555 fill(x, y, 1, height, !_textColor); 00556 ++x; 00557 } 00558 if (x >= _width) 00559 break; 00560 } 00561 } 00562 00575 int Bitmap::drawChar(int x, int y, char ch) 00576 { 00577 uint8_t height = fontHeight(_font); 00578 if (ch == ' ') { 00579 // Font may not have space, or it is zero-width. Calculate 00580 // the real size and fill the space. 00581 int spaceWidth = charWidth('n'); 00582 fill(x, y, spaceWidth, height, !_textColor); 00583 return spaceWidth; 00584 } 00585 uint8_t first = fontFirstChar(_font); 00586 uint8_t count = fontCharCount(_font); 00587 uint8_t index = (uint8_t)ch; 00588 if (index < first || index >= (first + count)) 00589 return 0; 00590 index -= first; 00591 uint8_t heightBytes = (height + 7) >> 3;; 00592 uint8_t width; 00593 const prog_uint8_t *image; 00594 if (fontIsFixed(_font)) { 00595 // Fixed-width font. 00596 width = fontWidth(_font); 00597 image = _font + 6 + index * heightBytes * width; 00598 } else { 00599 // Variable-width font. 00600 width = pgm_read_byte(_font + 6 + index); 00601 image = _font + 6 + count; 00602 for (uint8_t temp = 0; temp < index; ++temp) { 00603 // Scan through all previous characters to find the starting 00604 // location for this one. 00605 image += pgm_read_byte(_font + 6 + temp) * heightBytes; 00606 } 00607 } 00608 if ((x + width) <= 0 || (y + height) <= 0) 00609 return width; // Character is off the top or left of the screen. 00610 Color invColor = !_textColor; 00611 for (uint8_t cx = 0; cx < width; ++cx) { 00612 for (uint8_t cy = 0; cy < heightBytes; ++cy) { 00613 uint8_t value = pgm_read_byte(image + cy * width + cx); 00614 int posn; 00615 if (heightBytes > 1 && cy == (heightBytes - 1)) 00616 posn = height - 8; 00617 else 00618 posn = cy * 8; 00619 for (uint8_t bit = 0; bit < 8; ++bit) { 00620 if ((posn + bit) >= (cy * 8) && (posn + bit) <= height) { 00621 if (value & 0x01) 00622 setPixel(x + cx, y + posn + bit, _textColor); 00623 else 00624 setPixel(x + cx, y + posn + bit, invColor); 00625 } 00626 value >>= 1; 00627 } 00628 } 00629 } 00630 return width; 00631 } 00632 00640 int Bitmap::charWidth(char ch) const 00641 { 00642 uint8_t index = (uint8_t)ch; 00643 if (!_font) 00644 return 0; 00645 uint8_t first = fontFirstChar(_font); 00646 uint8_t count = fontCharCount(_font); 00647 if (index == ' ') 00648 index = 'n'; // In case the font does not contain space. 00649 if (index < first || index >= (first + count)) 00650 return 0; 00651 if (fontIsFixed(_font)) 00652 return fontWidth(_font); 00653 else 00654 return pgm_read_byte(_font + 6 + (index - first)); 00655 } 00656 00665 int Bitmap::textWidth(const char *str, int len) const 00666 { 00667 int width = 0; 00668 if (len < 0) 00669 len = strlen(str); 00670 while (len-- > 0) { 00671 width += charWidth(*str++); 00672 if (len > 0) 00673 ++width; 00674 } 00675 return width; 00676 } 00677 00687 int Bitmap::textWidth(const String &str, int start, int len) const 00688 { 00689 int width = 0; 00690 if (len < 0) 00691 len = str.length() - start; 00692 while (len-- > 0) { 00693 width += charWidth(str[start++]); 00694 if (len > 0) 00695 ++width; 00696 } 00697 return width; 00698 } 00699 00706 int Bitmap::textHeight() const 00707 { 00708 if (_font) 00709 return fontHeight(_font); 00710 else 00711 return 0; 00712 } 00713 00728 void Bitmap::copy(int x, int y, int width, int height, Bitmap *dest, int destX, int destY) 00729 { 00730 if (dest == this) { 00731 // Copying to within the same bitmap, so copy in a direction 00732 // that will prevent problems with overlap. 00733 blit(x, y, x + width - 1, y + height - 1, destX, destY); 00734 } else { 00735 // Copying to a different bitmap. 00736 while (height > 0) { 00737 for (int tempx = 0; tempx < width; ++tempx) 00738 dest->setPixel(destX + tempx, destY, pixel(x + tempx, y)); 00739 ++y; 00740 ++destY; 00741 --height; 00742 } 00743 } 00744 } 00745 00752 void Bitmap::fill(int x, int y, int width, int height, Color color) 00753 { 00754 while (height > 0) { 00755 for (int temp = 0; temp < width; ++temp) 00756 setPixel(x + temp, y, color); 00757 ++y; 00758 --height; 00759 } 00760 } 00761 00775 void Bitmap::fill(int x, int y, int width, int height, const prog_uint8_t *pattern, Color color) 00776 { 00777 uint8_t w = pgm_read_byte(pattern); 00778 uint8_t s = (w + 7) >> 3; 00779 uint8_t h = pgm_read_byte(pattern + 1); 00780 if (!w || !h) 00781 return; 00782 Color invColor = !color; 00783 for (int tempy = 0; tempy < height; ++tempy) { 00784 const prog_uint8_t *startLine = pattern + 2 + (tempy % h) * s; 00785 const prog_uint8_t *line = startLine; 00786 uint8_t mask = 0x80; 00787 uint8_t value = pgm_read_byte(line++); 00788 int bit = 0; 00789 for (int tempx = 0; tempx < width; ++tempx) { 00790 if (value & mask) 00791 setPixel(x + tempx, y + tempy, color); 00792 else 00793 setPixel(x + tempx, y + tempy, invColor); 00794 if (++bit >= w) { 00795 mask = 0x80; 00796 line = startLine; 00797 value = pgm_read_byte(line++); 00798 bit = 0; 00799 } else { 00800 mask >>= 1; 00801 if (!mask) { 00802 mask = 0x80; 00803 value = pgm_read_byte(line++); 00804 } 00805 } 00806 } 00807 } 00808 } 00809 00831 void Bitmap::scroll(int x, int y, int width, int height, int dx, int dy, Color fillColor) 00832 { 00833 // Bail out if no scrolling at all. 00834 if (!dx && !dy) 00835 return; 00836 00837 // Clamp the scroll region to the extents of the bitmap. 00838 if (x < 0) { 00839 width += x; 00840 x = 0; 00841 } 00842 if (y < 0) { 00843 height += y; 00844 y = 0; 00845 } 00846 if ((x + width) > _width) 00847 width = _width - x; 00848 if ((y + height) > _height) 00849 height = _height - y; 00850 if (width <= 0 || height <= 0) 00851 return; 00852 00853 // Scroll the region in the specified direction. 00854 if (dy < 0) { 00855 if (dx < 0) 00856 blit(x - dx, y - dy, x + width - 1 + dx, y + height - 1 + dy, x, y); 00857 else 00858 blit(x, y - dy, x + width - 1 - dx, y + height - 1 + dy, x + dx, y); 00859 } else { 00860 if (dx < 0) 00861 blit(x - dx, y, x + width - 1 + dx, y + height - 1 - dy, x, y + dy); 00862 else 00863 blit(x, y, x + width - 1 - dx, y + height - 1 - dy, x + dx, y + dy); 00864 } 00865 00866 // Fill the pixels that were uncovered by the scroll. 00867 if (dy < 0) { 00868 fill(x, y + height + dy, width, -dy, fillColor); 00869 if (dx < 0) 00870 fill(x + width + dx, y, -dx, height + dy, fillColor); 00871 else if (dx > 0) 00872 fill(x, y, dx, height + dy, fillColor); 00873 } else if (dy > 0) { 00874 fill(x, y, width, -dy, fillColor); 00875 if (dx < 0) 00876 fill(x + width + dx, y + dy, -dx, height - dy, fillColor); 00877 else if (dx > 0) 00878 fill(x, y + dy, dx, height - dy, fillColor); 00879 } else if (dx < 0) { 00880 fill(x + width + dx, y, -dx, height, fillColor); 00881 } else if (dx > 0) { 00882 fill(x, y, dx, height, fillColor); 00883 } 00884 } 00885 00892 void Bitmap::invert(int x, int y, int width, int height) 00893 { 00894 while (height > 0) { 00895 for (int tempx = x + width - 1; tempx >= x; --tempx) 00896 setPixel(tempx, y, !pixel(tempx, y)); 00897 --height; 00898 ++y; 00899 } 00900 } 00901 00902 void Bitmap::blit(int x1, int y1, int x2, int y2, int x3, int y3) 00903 { 00904 if (y3 < y1 || (y1 == y3 && x3 <= x1)) { 00905 for (int tempy = y1; tempy <= y2; ++tempy) { 00906 int y = y1 - tempy + y3; 00907 int x = x3 - x1; 00908 for (int tempx = x1; tempx <= x2; ++tempx) 00909 setPixel(x + tempx, y, pixel(tempx, tempy)); 00910 } 00911 } else { 00912 for (int tempy = y2; tempy >= y1; --tempy) { 00913 int y = y1 - tempy + y3; 00914 int x = x3 - x1; 00915 for (int tempx = x2; tempx >= x1; --tempx) 00916 setPixel(x + tempx, y, pixel(tempx, tempy)); 00917 } 00918 } 00919 } 00920 00921 void Bitmap::drawCirclePoints(int centerX, int centerY, int radius, int x, int y, Color borderColor, Color fillColor) 00922 { 00923 if (x != y) { 00924 setPixel(centerX + x, centerY + y, borderColor); 00925 setPixel(centerX + y, centerY + x, borderColor); 00926 setPixel(centerX + y, centerY - x, borderColor); 00927 setPixel(centerX + x, centerY - y, borderColor); 00928 setPixel(centerX - x, centerY - y, borderColor); 00929 setPixel(centerX - y, centerY - x, borderColor); 00930 setPixel(centerX - y, centerY + x, borderColor); 00931 setPixel(centerX - x, centerY + y, borderColor); 00932 if (fillColor != NoFill) { 00933 if (radius > 1) { 00934 drawLine(centerX - x + 1, centerY + y, centerX + x - 1, centerY + y, fillColor); 00935 drawLine(centerX - y + 1, centerY + x, centerX + y - 1, centerY + x, fillColor); 00936 drawLine(centerX - x + 1, centerY - y, centerX + x - 1, centerY - y, fillColor); 00937 drawLine(centerX - y + 1, centerY - x, centerX + y - 1, centerY - x, fillColor); 00938 } else if (radius == 1) { 00939 setPixel(centerX, centerY, fillColor); 00940 } 00941 } 00942 } else { 00943 setPixel(centerX + x, centerY + y, borderColor); 00944 setPixel(centerX + y, centerY - x, borderColor); 00945 setPixel(centerX - x, centerY - y, borderColor); 00946 setPixel(centerX - y, centerY + x, borderColor); 00947 if (fillColor != NoFill) { 00948 if (radius > 1) { 00949 drawLine(centerX - x + 1, centerY + y, centerX + x - 1, centerY + y, fillColor); 00950 drawLine(centerX - x + 1, centerY - y, centerX + x - 1, centerY - y, fillColor); 00951 } else if (radius == 1) { 00952 setPixel(centerX, centerY, fillColor); 00953 } 00954 } 00955 } 00956 }