ArduinoLibs
Bitmap.cpp
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 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator