A simple yet powerful library for controlling graphical displays. Multiple display controllers are supported using inheritance.
Dependents: mbed_rifletool Hexi_Bubble_Game Hexi_Catch-the-dot_Game Hexi_Acceleromagnetic_Synth
NOTE: This library is in beta right now. As far as I know, everything here works, but there are many features that are lacking so far. Most notably containers, button handling, and display drivers other than the SSD1306.
Diff: Abstracts/Canvas.cpp
- Revision:
- 0:b876cf091464
- Child:
- 1:f7003ec66a51
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Abstracts/Canvas.cpp Fri Aug 30 17:09:18 2013 +0000 @@ -0,0 +1,495 @@ +/* NeatGUI Library + * Copyright (c) 2013 Neil Thiessen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Canvas.h" + +Canvas::Canvas(int w, int h) +{ + m_Width = w; + m_Height = h; +} + +void Canvas::clear(unsigned int c) +{ + fillRect(0, 0, m_Width, m_Height, c); +} + +void Canvas::drawLine(int x0, int y0, int x1, int y1, unsigned int c) +{ + int steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + int temp = x0; + x0 = y0; + y0 = temp; + + temp = x1; + x1 = y1; + y1 = temp; + } + + if (x0 > x1) { + int temp = x0; + x0 = x1; + x1 = temp; + + temp = y0; + y0 = y1; + y1 = temp; + } + + int dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int err = dx / 2; + int ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0 <= x1; x0++) { + if (steep) { + drawPixel(y0, x0, c); + } else { + drawPixel(x0, y0, c); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +void Canvas::drawHLine(int x, int y, int w, unsigned int c) +{ + drawLine(x, y, x + w - 1, y, c); +} + +void Canvas::drawVLine(int x, int y, int h, unsigned int c) +{ + drawLine(x, y, x, y + h - 1, c); +} + +void Canvas::drawRect(int x, int y, int w, int h, unsigned int c) +{ + drawHLine(x, y, w, c); + drawHLine(x, y + h - 1, w, c); + drawVLine(x, y, h, c); + drawVLine(x + w - 1, y, h, c); +} + +void Canvas::fillRect(int x, int y, int w, int h, unsigned int c) +{ + for (int i = x; i < x + w; i++) { + drawVLine(i, y, h, c); + } +} + +void Canvas::drawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, unsigned int c) +{ + drawLine(x0, y0, x1, y1, c); + drawLine(x1, y1, x2, y2, c); + drawLine(x2, y2, x0, y0, c); +} + +void Canvas::fillTriangle(int x0, int y0, int x1, int y1, int x2, int y2, unsigned int c) +{ + int a, b, y, last; + + //Sort coordinates by Y order (y2 >= y1 >= y0) + if (y0 > y1) { + int temp = y0; + y0 = y1; + y1 = temp; + + temp = x0; + x0 = x1; + x1 = temp; + } + if (y1 > y2) { + int temp = y1; + x1 = y2; + y2 = temp; + + temp = x1; + x1 = x2; + x2 = temp; + } + if (y0 > y1) { + int temp = y0; + y0 = y1; + y1 = temp; + + temp = x0; + x0 = x1; + x1 = temp; + } + + //Handle awkward all-on-same-line case as its own thing + if(y0 == y2) { + a = b = x0; + if(x1 < a) + a = x1; + else if(x1 > b) + b = x1; + if(x2 < a) + a = x2; + else if(x2 > b) + b = x2; + + drawHLine(a, y0, b - a + 1, c); + + return; + } + + int dx01 = x1 - x0; + int dy01 = y1 - y0; + int dx02 = x2 - x0; + int dy02 = y2 - y0; + int dx12 = x2 - x1; + int dy12 = y2 - y1; + int sa = 0; + int sb = 0; + + //For upper part of triangle, find scanline crossings for segments + //0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 + //is included here (and second loop will be skipped, avoiding a /0 + //error there), otherwise scanline y1 is skipped here and handled + //in the second loop...which also avoids a /0 error here if y0=y1 + //(flat-topped triangle). + if(y1 == y2) + last = y1; //Include y1 scanline + else + last = y1 - 1; //Skip it + + for(y = y0; y <= last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + + if(a > b) { + int temp = a; + a = b; + b = temp; + } + drawHLine(a, y, b - a + 1, c); + } + + //For lower part of triangle, find scanline crossings for segments + //0-2 and 1-2. This loop is skipped if y1=y2. + sa = dx12 * (y - y1); + sb = dx02 * (y - y0); + for(; y <= y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + + if(a > b) { + int temp = a; + a = b; + b = temp; + } + drawHLine(a, y, b - a + 1, c); + } +} + +void Canvas::drawCircle(int x, int y, int r, unsigned int c) +{ + int f = 1 - r; + int ddF_x = 1; + int ddF_y = -2 * r; + int i = 0; + int j = r; + + drawPixel(x, y + r, c); + drawPixel(x, y - r, c); + drawPixel(x + r, y, c); + drawPixel(x - r, y, c); + + while(i < j) { + if(f >= 0) { + j--; + ddF_y += 2; + f += ddF_y; + } + i++; + ddF_x += 2; + f += ddF_x; + drawPixel(x + i, y + j, c); + drawPixel(x - i, y + j, c); + drawPixel(x + i, y - j, c); + drawPixel(x - i, y - j, c); + drawPixel(x + j, y + i, c); + drawPixel(x - j, y + i, c); + drawPixel(x + j, y - i, c); + drawPixel(x - j, y - i, c); + } +} + +void Canvas::fillCircle(int x, int y, int r, unsigned int c) +{ + drawVLine(x, y - r, 2 * r + 1, c); + fillCircleHelper(x, y, r, 3, 0, c); +} + +void Canvas::drawRoundRect(int x, int y, int w, int h, int r, unsigned int c) +{ + //Draw the four lines + drawHLine(x + r, y, w - 2 * r, c); //Top + drawHLine(x + r, y + h - 1, w - 2 * r, c); //Bottom + drawVLine(x, y + r, h - 2 * r, c); //Left + drawVLine(x + w - 1, y + r, h - 2 * r, c); //Right + + //Draw the four corners + drawCircleHelper(x + r, y + r, r, 1, c); + drawCircleHelper(x + w - r - 1, y + r, r, 2, c); + drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, c); + drawCircleHelper(x + r, y + h - r - 1, r, 8, c); +} + +void Canvas::fillRoundRect(int x, int y, int w, int h, int r, unsigned int c) +{ + //Draw the body + fillRect(x + r, y, w - 2 * r, h, c); + + //Draw the four corners + fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, c); + fillCircleHelper(x+r, y + r, r, 2, h - 2 * r - 1, c); +} + +void Canvas::drawImage(Image *img, int x, int y) +{ + for (int j = 0; j < img->height(); j++) { + for (int i = 0; i < img->width(); i++) { + drawPixel(i + x, j + y, img->pixel(i, j)); + } + } +} + +int Canvas::drawChar(char c, Font *fnt, int x, int y) +{ + //Get the character glyph + BitmapImage glyph = fnt->glyph(c); + + //Draw the character glyph + drawImage(&glyph, x, y); + + //Return the width of the glyph + return glyph.width(); +} + +void Canvas::drawString(const char *str, Font *fnt, int x, int y) +{ + int cursX = 0; + int cursY = 0; + + while(*str != NULL) { + //Check for a new line character + if (*str == '\n') { + //Go to a new line + cursX = 0; + cursY += fnt->height(); + + //We're done for this character + str++; + continue; + } + + //Check for a carriage return character + if (*str == '\r') { + //Ignore it, we're done for this character + str++; + continue; + } + + //Draw the character + cursX += drawChar(*str++, fnt, x + cursX, y + cursY); + } +} + +void Canvas::drawString(const char *str, Font *fnt, int x, int y, int w, int h) +{ + int cursX = 0; + int cursY = 0; + + while(*str != NULL) { + //Check for a new line character + if (*str == '\n') { + //Check if we can fit another line + if ((cursY + 2 * fnt->height()) < h) { + //Yes we can + cursX = 0; + cursY += fnt->height(); + + //We're done for this character + str++; + continue; + } else { + //Nope, we can't print any more so return + return; + } + } + + //Check for a carriage return character + if (*str == '\r') { + //Ignore it, we're done for this character + str++; + continue; + } + + //Check for entire words first + if ((*str > ' ') && (*str <= 0x7E)) { + //Draw entire word on canvas with correct wrapping + //int i = 0; + int wlen; + + //Determine the length of the next word + wlen = fnt->measureWord(str); + + //Will the length of the next word exceed the margins? + if ((wlen + cursX) > w) { + //Only do a newline if the word will fit on it + if (wlen <= w) { + //Check if we can fit another line + if ((cursY + 2 * fnt->height()) < h) { + //Yes we can + cursX = 0; + cursY += fnt->height(); + } else { + //Nope, we can't print any more so return + return; + } + } + } + + //Put just the word characters on the display up to the next non-whitespace character or the end of the string + while ((*str > ' ') && (*str <= 0x7E)) { + //Check if the character will fit on the screen + if ((fnt->glyph(*str).width() + cursX) > w) { + //Check if we can fit another line + if ((cursY + 2 * fnt->height()) < h) { + //Yes we can + cursX = 0; + cursY += fnt->height(); + } else { + //Nope, we can't print any more so return + return; + } + } + + //Draw the character + cursX += drawChar(*str++, fnt, x + cursX, y + cursY); + } + } else { + //Check if the character will fit on the screen + if ((fnt->glyph(*str).width() + cursX) > w) { + //Check if we can fit another line + if ((cursY + 2 * fnt->height()) < h) { + //Yes we can + cursX = 0; + cursY += fnt->height(); + } else { + //Nope, we can't print any more so return + return; + } + } + + //Draw the character + cursX += drawChar(*str++, fnt, x + cursX, y + cursY); + } + } +} + +int Canvas::width(void) +{ + return m_Width; +} + +int Canvas::height(void) +{ + return m_Height; +} + +void Canvas::drawCircleHelper(int x, int y, int r, unsigned int corner, unsigned int c) +{ + int f = 1 - r; + int ddF_x = 1; + int ddF_y = -2 * r; + int i = 0; + int j = r; + + while (i < j) { + if (f >= 0) { + j--; + ddF_y += 2; + f += ddF_y; + } + i++; + ddF_x += 2; + f += ddF_x; + if (corner & 0x4) { + drawPixel(x + i, y + j, c); + drawPixel(x + j, y + i, c); + } + if (corner & 0x2) { + drawPixel(x + i, y - j, c); + drawPixel(x + j, y - i, c); + } + if (corner & 0x8) { + drawPixel(x - j, y + i, c); + drawPixel(x - i, y + j, c); + } + if (corner & 0x1) { + drawPixel(x - j, y - i, c); + drawPixel(x - i, y - j, c); + } + } +} + +void Canvas::fillCircleHelper(int x, int y, int r, unsigned int corner, int delta, unsigned int c) +{ + int f = 1 - r; + int ddF_x = 1; + int ddF_y = -2 * r; + int i = 0; + int j = r; + + while (i < j) { + if (f >= 0) { + j--; + ddF_y += 2; + f += ddF_y; + } + i++; + ddF_x += 2; + f += ddF_x; + + if (corner & 0x1) { + drawVLine(x + i, y - j, 2 * j + 1 + delta, c); + drawVLine(x + j, y - i, 2 * i + 1 + delta, c); + } + if (corner & 0x2) { + drawVLine(x - i, y - j, 2 * j + 1 + delta, c); + drawVLine(x - j, y - i, 2 * i + 1 + delta, c); + } + } +}