drawer.cpp

00001
00002 /***************************************************************************
00003  *  drawer.cpp - Utility to draw in a buffer
00004  *
00005  *  Generated: Wed Feb 08 20:55:38 2006
00006  *  Copyright  2005-2007  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023
00024 #include <fvutils/draw/drawer.h>
00025 #include <fvutils/color/yuv.h>
00026
00027 #include <cmath>
00028 #include <algorithm>
00029 #include <unistd.h>
00030 
00031 /** @class Drawer <fvutils/draw/drawer.h>
00032  * Draw to an image.
00033  */
00034 
00035 /** Constructor.
00036  * Default paint color is white.
00037  */
00038 Drawer::Drawer()
00039 {
00040   __buffer = NULL;
00041   __color  = YUV_t::white();
00042 }
00043 
00044 /** Destructor */
00045 Drawer::~Drawer()
00046 {
00047 }
00048
00049 
00050 /** Set the buffer to draw to
00051  * @param buffer buffer to draw to, must be YUV422 planar formatted
00052  * @param width width of the buffer
00053  * @param height height of the buffer
00054  */
00055 void
00056 Drawer::set_buffer(unsigned char *buffer,
00057                   unsigned int width, unsigned int height)
00058 {
00059   this->__buffer     = buffer;
00060   this->__width      = width;
00061   this->__height     = height;
00062 }
00063
00064 
00065 /** Set drawing color.
00066  * @param y Y component of YUV drawing color
00067  * @param u U component of YUV drawing color
00068  * @param v V component of YUV drawing color
00069  */
00070 void
00071 Drawer::set_color(unsigned char y, unsigned char u, unsigned char v)
00072 {
00073   __color.Y = y;
00074   __color.U = u;
00075   __color.V = v;
00076 }
00077
00078 
00079 /** Set drawing color.
00080  * @param color the YUV drawing color
00081  */
00082 void
00083 Drawer::set_color(YUV_t color)
00084 {
00085   __color = color;
00086 }
00087
00088 
00089 /** Draw circle.
00090  * Draws a circle at the given center point and with the given radius.
00091  * @param center_x x coordinate of circle center
00092  * @param center_y y coordinate of circle center
00093  * @param radius radius of circle
00094  */
00095 void
00096 Drawer::draw_circle(int center_x, int center_y, unsigned int radius)
00097 {
00098
00099   if (__buffer == NULL) return;
00100
00101   unsigned int x  = 0,
00102                y  = radius,
00103                r2 = radius * radius;
00104
00105   unsigned char *up = YUV422_PLANAR_U_PLANE(__buffer, __width, __height);
00106   unsigned char *vp = YUV422_PLANAR_V_PLANE(__buffer, __width, __height);
00107
00108   unsigned int x_tmp, y_tmp, ind_tmp;
00109
00110   while (x <= y) {
00111
00112     x_tmp = center_x + x;
00113     y_tmp = center_y + y;
00114     if ( (x_tmp < __width) && (y_tmp < __height) ) {
00115       ind_tmp = y_tmp * __width + x_tmp;
00116       __buffer[ind_tmp]   = __color.Y;
00117       ind_tmp /= 2;
00118       up[ind_tmp] = __color.U;
00119       vp[ind_tmp] = __color.V;
00120     }
00121
00122     x_tmp = center_x - x;
00123     y_tmp = center_y + y;
00124     if ( (x_tmp < __width) && (y_tmp < __height) ) {
00125       ind_tmp = y_tmp * __width + x_tmp;
00126       __buffer[ind_tmp]   = __color.Y;
00127       ind_tmp /= 2;
00128       up[ind_tmp] = __color.U;
00129       vp[ind_tmp] = __color.V;
00130     }
00131
00132     x_tmp = center_x + y;
00133     y_tmp = center_y + x;
00134     if ( (x_tmp < __width) && (y_tmp < __height) ) {
00135       ind_tmp = y_tmp * __width + x_tmp;
00136       __buffer[ind_tmp]   = __color.Y;
00137       ind_tmp /= 2;
00138       up[ind_tmp] = __color.U;
00139       vp[ind_tmp] = __color.V;
00140     }
00141
00142     x_tmp = center_x - y;
00143     y_tmp = center_y + x;
00144     if ( (x_tmp < __width) && (y_tmp < __height) ) {
00145       ind_tmp = y_tmp * __width + x_tmp;
00146       __buffer[ind_tmp]   = __color.Y;
00147       ind_tmp /= 2;
00148       up[ind_tmp] = __color.U;
00149       vp[ind_tmp] = __color.V;
00150     }
00151
00152     x_tmp = center_x + x;
00153     y_tmp = center_y - y;
00154     if ( (x_tmp < __width) && (y_tmp < __height) ) {
00155       ind_tmp = y_tmp * __width + x_tmp;
00156       __buffer[ind_tmp]   = __color.Y;
00157       ind_tmp /= 2;
00158       up[ind_tmp] = __color.U;
00159       vp[ind_tmp] = __color.V;
00160     }
00161
00162     x_tmp = center_x - x;
00163     y_tmp = center_y - y;
00164     if ( (x_tmp < __width) && (y_tmp < __height)) {
00165       ind_tmp = y_tmp * __width + x_tmp;
00166       __buffer[ind_tmp]   = __color.Y;
00167       ind_tmp /= 2;
00168       up[ind_tmp] = __color.U;
00169       vp[ind_tmp] = __color.V;
00170     }
00171
00172     x_tmp = center_x + y;
00173     y_tmp = center_y - x;
00174     if ( (x_tmp < __width) && (y_tmp < __height)) {
00175       ind_tmp = y_tmp * __width + x_tmp;
00176       __buffer[ind_tmp]   = __color.Y;
00177       ind_tmp /= 2;
00178       up[ind_tmp] = __color.U;
00179       vp[ind_tmp] = __color.V;
00180     }
00181
00182     x_tmp = center_x - y;
00183     y_tmp = center_y - x;
00184     if ( (x_tmp < __width) && (y_tmp < __height) ) {
00185       ind_tmp = y_tmp * __width + x_tmp;
00186       __buffer[ind_tmp]   = __color.Y;
00187       ind_tmp /= 2;
00188       up[ind_tmp] = __color.U;
00189       vp[ind_tmp] = __color.V;
00190     }
00191
00192     ++x;
00193     y=(int)(sqrt((float)(r2 - x * x))+0.5);
00194   }
00195
00196 }
00197
00198 
00199 /** Draw rectangle.
00200  * @param x x coordinate of rectangle's upper left corner
00201  * @param y y coordinate of rectangle's upper left corner
00202  * @param w width of rectangle from x to the right
00203  * @param h height of rectangle from y to the bottom
00204  */
00205 void
00206 Drawer::draw_rectangle(unsigned int x, unsigned int y,
00207                       unsigned int w, unsigned int h)
00208 {
00209
00210   unsigned char *up = YUV422_PLANAR_U_PLANE(__buffer, __width, __height);
00211   unsigned char *vp = YUV422_PLANAR_V_PLANE(__buffer, __width, __height);
00212
00213   // horizontal line at top
00214   for (unsigned int i = x; i < x + w; ++i) {
00215     if ( i < __width ) {
00216       __buffer[ y * __width + i ]   = __color.Y;
00217       up[ (y * __width + i) / 2 ] = __color.U;
00218       vp[ (y * __width + i) / 2 ] = __color.V;
00219     } else {
00220       break;
00221     }
00222   }
00223
00224   // left and right
00225   for (unsigned int i = y; i < y + h; ++i) {
00226     // left
00227     __buffer[ i * __width + x ]   = __color.Y;
00228     up[ (i * __width + x) / 2 ] = __color.U;
00229     vp[ (i * __width + x) / 2 ] = __color.V;
00230
00231     if ( (x + w) < __width ) {
00232       // right
00233       __buffer[ i * __width + x + w ]   = __color.Y;
00234       up[ (i * __width + x + w) / 2 ] = __color.U;
00235       vp[ (i * __width + x + w) / 2 ] = __color.V;
00236     }
00237   }
00238
00239   // horizontal line at bottom
00240   for (unsigned int i = x; i < x + w; ++i) {
00241     if ( i < __width ) {
00242       __buffer[ (y + h) * __width + i ]   = __color.Y;
00243       up[ ((y + h) * __width + i) / 2 ] = __color.U;
00244       vp[ ((y + h) * __width + i) / 2 ] = __color.V;
00245     } else {
00246       break;
00247     }
00248   }
00249
00250 }
00251
00252 
00253 /** Draw inverted rectangle.
00254  * This draws a rectangle but instead of using the draw color it is drawn
00255  * in the inverted color of the pixel where it is drawn.
00256  * @param x x coordinate of rectangle's upper left corner
00257  * @param y y coordinate of rectangle's upper left corner
00258  * @param w width of rectangle from x to the right
00259  * @param h height of rectangle from y to the bottom
00260  */
00261 void
00262 Drawer::draw_rectangle_inverted(unsigned int x, unsigned int y,
00263                               unsigned int w, unsigned int h)
00264 {
00265
00266   unsigned int ind = 0;
00267
00268   // horizontal line at top
00269   for (unsigned int i = x; i < x + w; ++i) {
00270     if ( i < __width ) {
00271       ind = y * __width + i;
00272       __buffer[ind]   = 255 - __buffer[ind];
00273     } else {
00274       break;
00275     }
00276   }
00277
00278   // left and right
00279   for (unsigned int i = y; i < y + h; ++i) {
00280     // left
00281     ind = i * __width + x;
00282     __buffer[ind]   = 255 - __buffer[ind];
00283
00284     if ( (x + w) < __width ) {
00285       // right
00286       ind += w;
00287       __buffer[ind]   = 255 - __buffer[ind];
00288     }
00289   }
00290
00291   // horizontal line at bottom
00292   for (unsigned int i = x; i < x + w; ++i) {
00293     if ( i < __width ) {
00294       __buffer[ind]   = 255 - __buffer[ind];
00295     } else {
00296       break;
00297     }
00298   }
00299
00300 }
00301
00302 
00303 /** Draw point.
00304  * @param x x coordinate of point
00305  * @param y y coordinate of point
00306  */
00307 void
00308 Drawer::draw_point(unsigned int x, unsigned int y)
00309 {
00310   if ( x > __width) return;
00311   if ( y > __height) return;
00312
00313   unsigned char *up = YUV422_PLANAR_U_PLANE(__buffer, __width, __height);
00314   unsigned char *vp = YUV422_PLANAR_V_PLANE(__buffer, __width, __height);
00315
00316   __buffer[ y * __width + x ]   = __color.Y;
00317   up[ (y * __width + x) / 2 ] = __color.U;
00318   vp[ (y * __width + x) / 2 ] = __color.V;
00319 }
00320
00321 
00322 /** Color the given point.
00323  * This will leave the Y-component of the given pixel unchanged and will
00324  * just set the U and V components. This can be used to keep a little bit
00325  * of original image information but marking special regions.
00326  * @param x x coordinate of point
00327  * @param y y coordinate of point
00328  */
00329 void
00330 Drawer::color_point(unsigned int x, unsigned int y)
00331 {
00332   if ( x > __width) return;
00333   if ( y > __height) return;
00334
00335   unsigned char *up = YUV422_PLANAR_U_PLANE(__buffer, __width, __height);
00336   unsigned char *vp = YUV422_PLANAR_V_PLANE(__buffer, __width, __height);
00337
00338   __buffer[ y * __width + x ]   = __color.Y;
00339   up[ (y * __width + x) / 2 ] = __color.U;
00340   vp[ (y * __width + x) / 2 ] = __color.V;
00341 }
00342
00343 
00344 /** Color the given point.
00345  * This will color a single point (to save excessive function calls the color
00346  * is also a parameter)
00347  * @param x x coordinate of point
00348  * @param y y coordinate of point
00349  * @param color Color to set
00350  */
00351 void
00352 Drawer::color_point(unsigned int x, unsigned int y, YUV_t color)
00353 {
00354   if ( x > __width) return;
00355   if ( y > __height) return;
00356
00357   unsigned char *up = YUV422_PLANAR_U_PLANE(__buffer, __width, __height);
00358   unsigned char *vp = YUV422_PLANAR_V_PLANE(__buffer, __width, __height);
00359
00360   __buffer[ y * __width + x ] = color.Y;
00361   up[ (y * __width + x) / 2 ] = color.U;
00362   vp[ (y * __width + x) / 2 ] = color.V;
00363 }
00364
00365 
00366 /** Draw line.
00367  * Standard Bresenham in all directions. For in-depth information
00368  * have a look at http://de.wikipedia.org/wiki/Bresenham-Algorithmus
00369  * @param x_start x coordinate of start point
00370  * @param y_start y coordinate of start point
00371  * @param x_end x coordinate of end point
00372  * @param y_end y coordinate of end point
00373  */
00374 void
00375 Drawer::draw_line(unsigned int x_start, unsigned int y_start,
00376                  unsigned int x_end, unsigned int y_end)
00377 {
00378   /* heavily inspired by an article on German Wikipedia about
00379    * Bresenham's algorithm, confer
00380    * http://de.wikipedia.org/wiki/Bresenham-Algorithmus
00381    */
00382
00383
00384   int x, y, dist, xerr, yerr, dx, dy, incx, incy;
00385   bool was_inside_image = false;
00386
00387   unsigned char *up = YUV422_PLANAR_U_PLANE(__buffer, __width, __height);
00388   unsigned char *vp = YUV422_PLANAR_V_PLANE(__buffer, __width, __height);
00389
00390   // calculate distance in both directions
00391   dx = x_end - x_start;
00392   dy = y_end - y_start;
00393
00394   // Calculate sign of the increment
00395   if(dx < 0) {
00396     incx = -1;
00397     dx = -dx;
00398   } else {
00399     incx = dx ? 1 : 0;
00400   }
00401
00402   if(dy < 0) {
00403     incy = -1;
00404     dy = -dy;
00405   } else {
00406     incy = dy ? 1 : 0;
00407   }
00408
00409   // check which distance is larger
00410   dist = (dx > dy) ? dx : dy;
00411
00412   // Initialize for loops
00413   x = x_start;
00414   y = y_start;
00415   xerr = dx;
00416   yerr = dy;
00417
00418   /* Calculate and draw pixels */
00419   for(int t = 0; t < dist; ++t) {
00420     if ( ((unsigned int)x < __width) && ((unsigned int)y < __height) ) {
00421       if ( (x >= 0) && (y >= 0) ) {
00422         was_inside_image = true;
00423         __buffer[ y * __width + x ]   = __color.Y;
00424         up[ (y * __width + x) / 2 ] = __color.U;
00425         vp[ (y * __width + x) / 2 ] = __color.V;
00426       }
00427     } else {
00428       if ( was_inside_image ) {
00429         break;
00430       }
00431     }
00432
00433     xerr += dx;
00434     yerr += dy;
00435
00436     if(xerr > dist) {
00437       xerr -= dist;
00438       x += incx;
00439     }
00440
00441     if(yerr>dist) {
00442       yerr -= dist;
00443       y += incy;
00444     }
00445   }
00446
00447   if ( (x_end < __width) && (y_end < __height) ) {
00448     __buffer[ y_end * __width + x_end ]   = __color.Y;
00449     up[ (y_end * __width + x_end) / 2 ] = __color.U;
00450     vp[ (y_end * __width + x_end) / 2 ] = __color.V;
00451   }
00452
00453 }
00454 
00455 /** Draws a cross.
00456  * @param x_center Center of the cross
00457  * @param y_center Center of the cross
00458  * @param width of the bars
00459  */
00460 void
00461 Drawer::draw_cross(unsigned int x_center, unsigned int y_center, unsigned int width)
00462 {
00463   x_center = std::min(x_center, __width);
00464   y_center = std::min(y_center, __height);
00465
00466   int r = width / 2;
00467   unsigned int a = std::max(0, (int)x_center - r);
00468   unsigned int b = std::min(x_center + r, __width);
00469   draw_line(a, y_center, b, y_center);
00470
00471   a = std::max(0, (int)y_center - r);
00472   b = std::min(y_center + r, __height);
00473   draw_line(x_center, a, x_center, b);
00474 }
00475