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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Canvas.cpp Source File

Canvas.cpp

00001 /* NeatGUI Library
00002  * Copyright (c) 2013 Neil Thiessen
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "Canvas.h"
00018 
00019 Canvas::Canvas(int w, int h)
00020 {
00021     m_Width = w;
00022     m_Height = h;
00023 }
00024 
00025 void Canvas::clear(unsigned int c)
00026 {
00027     fillRect(0, 0, m_Width, m_Height, c);
00028 }
00029 
00030 void Canvas::drawLine(int x0, int y0, int x1, int y1, unsigned int c)
00031 {
00032     int steep = abs(y1 - y0) > abs(x1 - x0);
00033     if (steep) {
00034         int temp = x0;
00035         x0 = y0;
00036         y0 = temp;
00037 
00038         temp = x1;
00039         x1 = y1;
00040         y1 = temp;
00041     }
00042 
00043     if (x0 > x1) {
00044         int temp = x0;
00045         x0 = x1;
00046         x1 = temp;
00047 
00048         temp = y0;
00049         y0 = y1;
00050         y1 = temp;
00051     }
00052 
00053     int dx, dy;
00054     dx = x1 - x0;
00055     dy = abs(y1 - y0);
00056 
00057     int err = dx / 2;
00058     int ystep;
00059 
00060     if (y0 < y1) {
00061         ystep = 1;
00062     } else {
00063         ystep = -1;
00064     }
00065 
00066     for (; x0 <= x1; x0++) {
00067         if (steep) {
00068             drawPixel(y0, x0, c);
00069         } else {
00070             drawPixel(x0, y0, c);
00071         }
00072         err -= dy;
00073         if (err < 0) {
00074             y0 += ystep;
00075             err += dx;
00076         }
00077     }
00078 }
00079 
00080 void Canvas::drawHLine(int x, int y, int w, unsigned int c)
00081 {
00082     drawLine(x, y, x + w - 1, y, c);
00083 }
00084 
00085 void Canvas::drawVLine(int x, int y, int h, unsigned int c)
00086 {
00087     drawLine(x, y, x, y + h - 1, c);
00088 }
00089 
00090 void Canvas::drawRect(int x, int y, int w, int h, unsigned int c)
00091 {
00092     drawHLine(x, y, w, c);
00093     drawHLine(x, y + h - 1, w, c);
00094     drawVLine(x, y, h, c);
00095     drawVLine(x + w - 1, y, h, c);
00096 }
00097 
00098 void Canvas::fillRect(int x, int y, int w, int h, unsigned int c)
00099 {
00100     for (int i = x; i < x + w; i++) {
00101         drawVLine(i, y, h, c);
00102     }
00103 }
00104 
00105 void Canvas::drawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, unsigned int c)
00106 {
00107     drawLine(x0, y0, x1, y1, c);
00108     drawLine(x1, y1, x2, y2, c);
00109     drawLine(x2, y2, x0, y0, c);
00110 }
00111 
00112 void Canvas::fillTriangle(int x0, int y0, int x1, int y1, int x2, int y2, unsigned int c)
00113 {
00114     int a, b, y, last;
00115 
00116     //Sort coordinates by Y order (y2 >= y1 >= y0)
00117     if (y0 > y1) {
00118         int temp = y0;
00119         y0 = y1;
00120         y1 = temp;
00121 
00122         temp = x0;
00123         x0 = x1;
00124         x1 = temp;
00125     }
00126     if (y1 > y2) {
00127         int temp = y1;
00128         x1 = y2;
00129         y2 = temp;
00130 
00131         temp = x1;
00132         x1 = x2;
00133         x2 = temp;
00134     }
00135     if (y0 > y1) {
00136         int temp = y0;
00137         y0 = y1;
00138         y1 = temp;
00139 
00140         temp = x0;
00141         x0 = x1;
00142         x1 = temp;
00143     }
00144 
00145     //Handle awkward all-on-same-line case as its own thing
00146     if(y0 == y2) {
00147         a = b = x0;
00148         if(x1 < a)
00149             a = x1;
00150         else if(x1 > b)
00151             b = x1;
00152         if(x2 < a)
00153             a = x2;
00154         else if(x2 > b)
00155             b = x2;
00156 
00157         drawHLine(a, y0, b - a + 1, c);
00158 
00159         return;
00160     }
00161 
00162     int dx01 = x1 - x0;
00163     int dy01 = y1 - y0;
00164     int dx02 = x2 - x0;
00165     int dy02 = y2 - y0;
00166     int dx12 = x2 - x1;
00167     int dy12 = y2 - y1;
00168     int sa = 0;
00169     int sb = 0;
00170 
00171     //For upper part of triangle, find scanline crossings for segments
00172     //0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
00173     //is included here (and second loop will be skipped, avoiding a /0
00174     //error there), otherwise scanline y1 is skipped here and handled
00175     //in the second loop...which also avoids a /0 error here if y0=y1
00176     //(flat-topped triangle).
00177     if(y1 == y2)
00178         last = y1;      //Include y1 scanline
00179     else
00180         last = y1 - 1;  //Skip it
00181 
00182     for(y = y0; y <= last; y++) {
00183         a = x0 + sa / dy01;
00184         b = x0 + sb / dy02;
00185         sa += dx01;
00186         sb += dx02;
00187 
00188         if(a > b) {
00189             int temp = a;
00190             a = b;
00191             b = temp;
00192         }
00193         drawHLine(a, y, b - a + 1, c);
00194     }
00195 
00196     //For lower part of triangle, find scanline crossings for segments
00197     //0-2 and 1-2.  This loop is skipped if y1=y2.
00198     sa = dx12 * (y - y1);
00199     sb = dx02 * (y - y0);
00200     for(; y <= y2; y++) {
00201         a = x1 + sa / dy12;
00202         b = x0 + sb / dy02;
00203         sa += dx12;
00204         sb += dx02;
00205 
00206         if(a > b) {
00207             int temp = a;
00208             a = b;
00209             b = temp;
00210         }
00211         drawHLine(a, y, b - a + 1, c);
00212     }
00213 }
00214 
00215 void Canvas::drawCircle(int x, int y, int r, unsigned int c)
00216 {
00217     int f = 1 - r;
00218     int ddF_x = 1;
00219     int ddF_y = -2 * r;
00220     int i = 0;
00221     int j = r;
00222 
00223     drawPixel(x, y + r, c);
00224     drawPixel(x, y - r, c);
00225     drawPixel(x + r, y, c);
00226     drawPixel(x - r, y, c);
00227 
00228     while(i < j) {
00229         if(f >= 0) {
00230             j--;
00231             ddF_y += 2;
00232             f += ddF_y;
00233         }
00234         i++;
00235         ddF_x += 2;
00236         f += ddF_x;
00237         drawPixel(x + i, y + j, c);
00238         drawPixel(x - i, y + j, c);
00239         drawPixel(x + i, y - j, c);
00240         drawPixel(x - i, y - j, c);
00241         drawPixel(x + j, y + i, c);
00242         drawPixel(x - j, y + i, c);
00243         drawPixel(x + j, y - i, c);
00244         drawPixel(x - j, y - i, c);
00245     }
00246 }
00247 
00248 void Canvas::fillCircle(int x, int y, int r, unsigned int c)
00249 {
00250     drawVLine(x, y - r, 2 * r + 1, c);
00251     fillCircleHelper(x, y, r, 3, 0, c);
00252 }
00253 
00254 void Canvas::drawRoundRect(int x, int y, int w, int h, int r, unsigned int c)
00255 {
00256     //Draw the four lines
00257     drawHLine(x + r, y, w - 2 * r, c);            //Top
00258     drawHLine(x + r, y + h - 1, w - 2 * r, c);    //Bottom
00259     drawVLine(x, y + r, h - 2 * r, c);            //Left
00260     drawVLine(x + w - 1, y + r, h - 2 * r, c);    //Right
00261 
00262     //Draw the four corners
00263     drawCircleHelper(x + r, y + r, r, 1, c);
00264     drawCircleHelper(x + w - r - 1, y + r, r, 2, c);
00265     drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, c);
00266     drawCircleHelper(x + r, y + h - r - 1, r, 8, c);
00267 }
00268 
00269 void Canvas::fillRoundRect(int x, int y, int w, int h, int r, unsigned int c)
00270 {
00271     //Draw the body
00272     fillRect(x + r, y, w - 2 * r, h, c);
00273 
00274     //Draw the four corners
00275     fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, c);
00276     fillCircleHelper(x+r, y + r, r, 2, h - 2 * r - 1, c);
00277 }
00278 
00279 void Canvas::drawImage(Image* img, int x, int y)
00280 {
00281     for (int j = 0; j < img->height(); j++) {
00282         for (int i = 0; i < img->width(); i++) {
00283             drawPixel(i + x, j + y, img->pixel(i, j));
00284         }
00285     }
00286 }
00287 
00288 int Canvas::drawChar(char c, Font* fnt, int x, int y)
00289 {
00290     //Get the character glyph
00291     BitmapImage glyph = fnt->glyph(c);
00292 
00293     //Draw the character glyph
00294     drawImage(&glyph, x, y);
00295 
00296     //Return the width of the glyph
00297     return glyph.width();
00298 }
00299 
00300 void Canvas::drawString(const char* str, Font* fnt, int x, int y)
00301 {
00302     int cursX = 0;
00303     int cursY = 0;
00304 
00305     while(*str != NULL) {
00306         //Check for a new line character
00307         if (*str == '\n') {
00308             //Go to a new line
00309             cursX = 0;
00310             cursY += fnt->height();
00311 
00312             //We're done for this character
00313             str++;
00314             continue;
00315         }
00316 
00317         //Check for a carriage return character
00318         if (*str == '\r') {
00319             //Ignore it, we're done for this character
00320             str++;
00321             continue;
00322         }
00323 
00324         //Draw the character
00325         cursX += drawChar(*str++, fnt, x + cursX, y + cursY);
00326     }
00327 }
00328 
00329 void Canvas::drawString(const char* str, Font* fnt, int x, int y, int w, int h)
00330 {
00331     int cursX = 0;
00332     int cursY = 0;
00333 
00334     while(*str != NULL) {
00335         //Check for a new line character
00336         if (*str == '\n') {
00337             //Check if we can fit another line
00338             if ((cursY + 2 * fnt->height()) < h) {
00339                 //Yes we can
00340                 cursX = 0;
00341                 cursY += fnt->height();
00342 
00343                 //We're done for this character
00344                 str++;
00345                 continue;
00346             } else {
00347                 //Nope, we can't print any more so return
00348                 return;
00349             }
00350         }
00351 
00352         //Check for a carriage return character
00353         if (*str == '\r') {
00354             //Ignore it, we're done for this character
00355             str++;
00356             continue;
00357         }
00358 
00359         //Check for entire words first
00360         if ((*str > ' ') && (*str <= 0x7E)) {
00361             //Draw entire word on canvas with correct wrapping
00362             //int i = 0;
00363             int wlen;
00364 
00365             //Determine the length of the next word
00366             wlen = fnt->measureWord(str);
00367 
00368             //Will the length of the next word exceed the margins?
00369             if ((wlen + cursX) > w) {
00370                 //Only do a newline if the word will fit on it
00371                 if (wlen <= w) {
00372                     //Check if we can fit another line
00373                     if ((cursY + 2 * fnt->height()) < h) {
00374                         //Yes we can
00375                         cursX = 0;
00376                         cursY += fnt->height();
00377                     } else {
00378                         //Nope, we can't print any more so return
00379                         return;
00380                     }
00381                 }
00382             }
00383 
00384             //Put just the word characters on the display up to the next non-whitespace character or the end of the string
00385             while ((*str > ' ') && (*str <= 0x7E)) {
00386                 //Check if the character will fit on the screen
00387                 if ((fnt->glyph(*str).width() + cursX) > w) {
00388                     //Check if we can fit another line
00389                     if ((cursY + 2 * fnt->height()) < h) {
00390                         //Yes we can
00391                         cursX = 0;
00392                         cursY += fnt->height();
00393                     } else {
00394                         //Nope, we can't print any more so return
00395                         return;
00396                     }
00397                 }
00398 
00399                 //Draw the character
00400                 cursX += drawChar(*str++, fnt, x + cursX, y + cursY);
00401             }
00402         } else {
00403             //Check if the character will fit on the screen
00404             if ((fnt->glyph(*str).width() + cursX) > w) {
00405                 //Check if we can fit another line
00406                 if ((cursY + 2 * fnt->height()) < h) {
00407                     //Yes we can
00408                     cursX = 0;
00409                     cursY += fnt->height();
00410                 } else {
00411                     //Nope, we can't print any more so return
00412                     return;
00413                 }
00414             }
00415 
00416             //Draw the character
00417             cursX += drawChar(*str++, fnt, x + cursX, y + cursY);
00418         }
00419     }
00420 }
00421 
00422 int Canvas::width()
00423 {
00424     return m_Width;
00425 }
00426 
00427 int Canvas::height()
00428 {
00429     return m_Height;
00430 }
00431 
00432 void Canvas::drawCircleHelper(int x, int y, int r, unsigned int corner, unsigned int c)
00433 {
00434     int f = 1 - r;
00435     int ddF_x = 1;
00436     int ddF_y = -2 * r;
00437     int i = 0;
00438     int j = r;
00439 
00440     while (i < j) {
00441         if (f >= 0) {
00442             j--;
00443             ddF_y += 2;
00444             f += ddF_y;
00445         }
00446         i++;
00447         ddF_x += 2;
00448         f += ddF_x;
00449         if (corner & 0x4) {
00450             drawPixel(x + i, y + j, c);
00451             drawPixel(x + j, y + i, c);
00452         }
00453         if (corner & 0x2) {
00454             drawPixel(x + i, y - j, c);
00455             drawPixel(x + j, y - i, c);
00456         }
00457         if (corner & 0x8) {
00458             drawPixel(x - j, y + i, c);
00459             drawPixel(x - i, y + j, c);
00460         }
00461         if (corner & 0x1) {
00462             drawPixel(x - j, y - i, c);
00463             drawPixel(x - i, y - j, c);
00464         }
00465     }
00466 }
00467 
00468 void Canvas::fillCircleHelper(int x, int y, int r, unsigned int corner, int delta, unsigned int c)
00469 {
00470     int f = 1 - r;
00471     int ddF_x = 1;
00472     int ddF_y = -2 * r;
00473     int i = 0;
00474     int j = r;
00475 
00476     while (i < j) {
00477         if (f >= 0) {
00478             j--;
00479             ddF_y += 2;
00480             f += ddF_y;
00481         }
00482         i++;
00483         ddF_x += 2;
00484         f += ddF_x;
00485 
00486         if (corner & 0x1) {
00487             drawVLine(x + i, y - j, 2 * j + 1 + delta, c);
00488             drawVLine(x + j, y - i, 2 * i + 1 + delta, c);
00489         }
00490         if (corner & 0x2) {
00491             drawVLine(x - i, y - j, 2 * j + 1 + delta, c);
00492             drawVLine(x - j, y - i, 2 * i + 1 + delta, c);
00493         }
00494     }
00495 }