// Copyright (C) 2005 Davis E. King (davis@dlib.net), and Nils Labugt // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_GUI_CANVAS_DRAWINg_ #define DLIB_GUI_CANVAS_DRAWINg_ #include "canvas_drawing_abstract.h" #include "../gui_core.h" #include "../algs.h" #include "../array2d.h" #include "../pixel.h" #include "../image_transforms/assign_image.h" #include "../geometry.h" #include <cmath> namespace dlib { // ---------------------------------------------------------------------------------------- template <typename pixel_type> void draw_line ( const canvas& c, const point& p1, const point& p2, const pixel_type& pixel, const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { rectangle valid_area(c.intersect(area)); long x1 = p1.x(); long y1 = p1.y(); long x2 = p2.x(); long y2 = p2.y(); if (x1 == x2) { // if the x coordinate is inside the canvas's area if (x1 <= valid_area.right() && x1 >= valid_area.left()) { // make sure y1 comes before y2 if (y1 > y2) swap(y1,y2); y1 = std::max(y1,valid_area.top()); y2 = std::min(y2,valid_area.bottom()); // this is a vertical line for (long y = y1; y <= y2; ++y) { assign_pixel(c[y-c.top()][x1-c.left()], pixel); } } } else if (y1 == y2) { // if the y coordinate is inside the canvas's area if (y1 <= valid_area.bottom() && y1 >= valid_area.top()) { // make sure x1 comes before x2 if (x1 > x2) swap(x1,x2); x1 = std::max(x1,valid_area.left()); x2 = std::min(x2,valid_area.right()); // this is a horizontal line for (long x = x1; x <= x2; ++x) { assign_pixel(c[y1-c.top()][x-c.left()], pixel); } } } else { rgb_alpha_pixel alpha_pixel; assign_pixel(alpha_pixel, pixel); const unsigned char max_alpha = alpha_pixel.alpha; const long rise = (((long)y2) - ((long)y1)); const long run = (((long)x2) - ((long)x1)); if (std::abs(rise) < std::abs(run)) { const double slope = ((double)rise)/run; double first, last; if (x1 > x2) { first = std::max(x2,valid_area.left()); last = std::min(x1,valid_area.right()); } else { first = std::max(x1,valid_area.left()); last = std::min(x2,valid_area.right()); } long y; long x; const double x1f = x1; const double y1f = y1; for (double i = first; i <= last; ++i) { const double dy = slope*(i-x1f) + y1f; const double dx = i; y = static_cast<long>(dy); x = static_cast<long>(dx); if (y >= valid_area.top() && y <= valid_area.bottom()) { alpha_pixel.alpha = static_cast<unsigned char>((1.0-(dy-y))*max_alpha); assign_pixel(c[y-c.top()][x-c.left()], alpha_pixel); } if (y+1 >= valid_area.top() && y+1 <= valid_area.bottom()) { alpha_pixel.alpha = static_cast<unsigned char>((dy-y)*max_alpha); assign_pixel(c[y+1-c.top()][x-c.left()], alpha_pixel); } } } else { const double slope = ((double)run)/rise; double first, last; if (y1 > y2) { first = std::max(y2,valid_area.top()); last = std::min(y1,valid_area.bottom()); } else { first = std::max(y1,valid_area.top()); last = std::min(y2,valid_area.bottom()); } long x; long y; const double x1f = x1; const double y1f = y1; for (double i = first; i <= last; ++i) { const double dx = slope*(i-y1f) + x1f; const double dy = i; y = static_cast<long>(dy); x = static_cast<long>(dx); if (x >= valid_area.left() && x <= valid_area.right()) { alpha_pixel.alpha = static_cast<unsigned char>((1.0-(dx-x))*max_alpha); assign_pixel(c[y-c.top()][x-c.left()], alpha_pixel); } if (x+1 >= valid_area.left() && x+1 <= valid_area.right()) { alpha_pixel.alpha = static_cast<unsigned char>((dx-x)*max_alpha); assign_pixel(c[y-c.top()][x+1-c.left()], alpha_pixel); } } } } } inline void draw_line ( const canvas& c, const point& p1, const point& p2 ){ draw_line(c,p1,p2,0); } // ---------------------------------------------------------------------------------------- void draw_sunken_rectangle ( const canvas& c, const rectangle& border, unsigned char alpha = 255 ); // ---------------------------------------------------------------------------------------- template <typename pixel_type> inline void draw_pixel ( const canvas& c, const point& p, const pixel_type& pixel ) { if (c.contains(p)) { assign_pixel(c[p.y()-c.top()][p.x()-c.left()],pixel); } } // ---------------------------------------------------------------------------------------- template <typename pixel_type> void draw_checkered ( const canvas& c, const rectangle& a, const pixel_type& pixel1, const pixel_type& pixel2 ) { rectangle area = a.intersect(c); if (area.is_empty()) return; for (long i = area.left(); i <= area.right(); ++i) { for (long j = area.top(); j <= area.bottom(); ++j) { canvas::pixel& p = c[j - c.top()][i - c.left()]; if ((j&0x1) ^ (i&0x1)) { assign_pixel(p,pixel1); } else { assign_pixel(p,pixel2); } } } } // ---------------------------------------------------------------------------------------- void draw_button_down ( const canvas& c, const rectangle& btn, unsigned char alpha = 255 ); // ---------------------------------------------------------------------------------------- void draw_button_up ( const canvas& c, const rectangle& btn, unsigned char alpha = 255 ); // ---------------------------------------------------------------------------------------- template <typename pixel_type> void draw_circle ( const canvas& c, const point& center_point, double radius, const pixel_type& pixel, const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { using std::sqrt; rectangle valid_area(c.intersect(area)); const long x = center_point.x(); const long y = center_point.y(); if (radius > 1) { long first_x = static_cast<long>(x - radius + 0.5); long last_x = static_cast<long>(x + radius + 0.5); const double rs = radius*radius; // ensure that we only loop over the part of the x dimension that this // canvas contains. if (first_x < valid_area.left()) first_x = valid_area.left(); if (last_x > valid_area.right()) last_x = valid_area.right(); long top, bottom; top = static_cast<long>(sqrt(std::max(rs - (first_x-x-0.5)*(first_x-x-0.5),0.0))+0.5); top += y; long last = top; // draw the left half of the circle long middle = std::min(x-1,last_x); for (long i = first_x; i <= middle; ++i) { double a = i - x + 0.5; // find the top of the arc top = static_cast<long>(sqrt(std::max(rs - a*a,0.0))+0.5); top += y; long temp = top; while(top >= last) { bottom = y - top + y; if (top >= valid_area.top() && top <= valid_area.bottom() ) { assign_pixel(c[top-c.top()][i-c.left()],pixel); } if (bottom >= valid_area.top() && bottom <= valid_area.bottom() ) { assign_pixel(c[bottom-c.top()][i-c.left()],pixel); } --top; } last = temp; } middle = std::max(x,first_x); top = static_cast<long>(sqrt(std::max(rs - (last_x-x+0.5)*(last_x-x+0.5),0.0))+0.5); top += y; last = top; // draw the right half of the circle for (long i = last_x; i >= middle; --i) { double a = i - x - 0.5; // find the top of the arc top = static_cast<long>(sqrt(std::max(rs - a*a,0.0))+0.5); top += y; long temp = top; while(top >= last) { bottom = y - top + y; if (top >= valid_area.top() && top <= valid_area.bottom() ) { assign_pixel(c[top-c.top()][i-c.left()],pixel); } if (bottom >= valid_area.top() && bottom <= valid_area.bottom() ) { assign_pixel(c[bottom-c.top()][i-c.left()],pixel); } --top; } last = temp; } } else if (radius == 1 && x >= valid_area.left() && x <= valid_area.right() && y >= valid_area.top() && y <= valid_area.bottom() ) { assign_pixel(c[y-c.top()][x-c.left()], pixel); } } inline void draw_circle ( const canvas& c, const point& center_point, double radius ){ draw_circle(c, center_point, radius, 0); } // ---------------------------------------------------------------------------------------- template <typename pixel_type> void draw_solid_circle ( const canvas& c, const point& center_point, double radius, const pixel_type& pixel, const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { using std::sqrt; rectangle valid_area(c.intersect(area)); const long x = center_point.x(); const long y = center_point.y(); if (radius > 1) { long first_x = static_cast<long>(x - radius + 0.5); long last_x = static_cast<long>(x + radius + 0.5); const double rs = radius*radius; // ensure that we only loop over the part of the x dimension that this // canvas contains. if (first_x < valid_area.left()) first_x = valid_area.left(); if (last_x > valid_area.right()) last_x = valid_area.right(); long top, bottom; top = static_cast<long>(sqrt(std::max(rs - (first_x-x-0.5)*(first_x-x-0.5),0.0))+0.5); top += y; long last = top; // draw the left half of the circle long middle = std::min(x-1,last_x); for (long i = first_x; i <= middle; ++i) { double a = i - x + 0.5; // find the top of the arc top = static_cast<long>(sqrt(std::max(rs - a*a,0.0))+0.5); top += y; long temp = top; while(top >= last) { bottom = y - top + y; draw_line(c, point(i,top),point(i,bottom),pixel,area); --top; } last = temp; } middle = std::max(x,first_x); top = static_cast<long>(sqrt(std::max(rs - (last_x-x+0.5)*(last_x-x+0.5),0.0))+0.5); top += y; last = top; // draw the right half of the circle for (long i = last_x; i >= middle; --i) { double a = i - x - 0.5; // find the top of the arc top = static_cast<long>(sqrt(std::max(rs - a*a,0.0))+0.5); top += y; long temp = top; while(top >= last) { bottom = y - top + y; draw_line(c, point(i,top),point(i,bottom),pixel,area); --top; } last = temp; } } else if (radius == 1 && x >= valid_area.left() && x <= valid_area.right() && y >= valid_area.top() && y <= valid_area.bottom() ) { assign_pixel(c[y-c.top()][x-c.left()], pixel); } } inline void draw_solid_circle ( const canvas& c, const point& center_point, double radius ) { draw_solid_circle(c, center_point, radius, 0); } // ---------------------------------------------------------------------------------------- namespace impl { template <typename alloc> void get_convex_polygon_shape ( const std::vector<point>& points, const long top, const long bottom, std::vector<double,alloc>& left_boundary, std::vector<double,alloc>& right_boundary ) /*! requires - 0 <= top <= bottom ensures - interprets points as the coordinates defining a convex polygon. In particular, we interpret points as a list of the vertices of the polygon and assume they are ordered in clockwise order. - #left_boundary.size() == bottom-top+1 - #right_boundary.size() == bottom-top+1 - for all top <= y <= bottom: - #left_boundary[y-top] == the x coordinate for the left most side of the polygon at coordinate y. - #right_boundary[y-top] == the x coordinate for the right most side of the polygon at coordinate y. !*/ { using std::min; using std::max; left_boundary.assign(bottom-top+1, std::numeric_limits<double>::infinity()); right_boundary.assign(bottom-top+1, -std::numeric_limits<double>::infinity()); // trace out the points along the edge of the polynomial and record them for (unsigned long i = 0; i < points.size(); ++i) { const point p1 = points[i]; const point p2 = points[(i+1)%points.size()]; if (p1.y() == p2.y()) { if (top <= p1.y() && p1.y() <= bottom) { const long y = p1.y() - top; const double xmin = min(p1.x(), p2.x()); const double xmax = min(p1.x(), p2.x()); left_boundary[y] = min(left_boundary[y], xmin); right_boundary[y] = max(right_boundary[y], xmax); } } else { // Here we trace out the line from p1 to p2 and record where it hits. // x = m*y + b const double m = (p2.x() - p1.x())/(double)(p2.y()-p1.y()); const double b = p1.x() - m*p1.y(); // because: x1 = m*y1 + b const long ymin = max(top,min(p1.y(), p2.y())); const long ymax = min(bottom,max(p1.y(), p2.y())); for (long y = ymin; y <= ymax; ++y) { const double x = m*y + b; const unsigned long idx = y-top; left_boundary[idx] = min(left_boundary[idx], x); right_boundary[idx] = max(right_boundary[idx], x); } } } } // ------------------------------------------------------------------------------------ } template <typename pixel_type> void draw_solid_convex_polygon ( const canvas& c, const std::vector<point>& polygon, const pixel_type& pixel, const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { using std::max; using std::min; const rectangle valid_area(c.intersect(area)); rectangle bounding_box; for (unsigned long i = 0; i < polygon.size(); ++i) bounding_box += polygon[i]; // Don't do anything if the polygon is totally outside the area we can draw in // right now. if (bounding_box.intersect(valid_area).is_empty()) return; rgb_alpha_pixel alpha_pixel; assign_pixel(alpha_pixel, pixel); const unsigned char max_alpha = alpha_pixel.alpha; // we will only want to loop over the part of left_boundary that is part of the // valid_area. long top = max(valid_area.top(),bounding_box.top()); long bottom = min(valid_area.bottom(),bounding_box.bottom()); // Since we look at the adjacent rows of boundary information when doing the alpha // blending, we want to make sure we always have some boundary information unless // we are at the absolute edge of the polygon. const long top_offset = (top == bounding_box.top()) ? 0 : 1; const long bottom_offset = (bottom == bounding_box.bottom()) ? 0 : 1; if (top != bounding_box.top()) top -= 1; if (bottom != bounding_box.bottom()) bottom += 1; std::vector<double> left_boundary; std::vector<double> right_boundary; impl::get_convex_polygon_shape(polygon, top, bottom, left_boundary, right_boundary); // draw the polygon row by row for (unsigned long i = top_offset; i < left_boundary.size(); ++i) { long left_x = static_cast<long>(std::ceil(left_boundary[i])); long right_x = static_cast<long>(std::floor(right_boundary[i])); left_x = max(left_x, valid_area.left()); right_x = min(right_x, valid_area.right()); if (i < left_boundary.size()-bottom_offset) { // draw the main body of the polygon for (long x = left_x; x <= right_x; ++x) { const long y = i+top; assign_pixel(c[y-c.top()][x-c.left()], pixel); } } if (i == 0) continue; // Now draw anti-aliased edges so they don't look all pixely. // Alpha blend the edges on the left side. double delta = left_boundary[i-1] - left_boundary[i]; if (std::abs(delta) <= 1) { if (std::floor(left_boundary[i]) != left_x) { const point p(static_cast<long>(std::floor(left_boundary[i])), i+top); rgb_alpha_pixel temp = alpha_pixel; temp.alpha = max_alpha-static_cast<unsigned char>((left_boundary[i]-p.x())*max_alpha); if (valid_area.contains(p)) assign_pixel(c[p.y()-c.top()][p.x()-c.left()],temp); } } else if (delta < 0) // on the bottom side { for (long x = static_cast<long>(std::ceil(left_boundary[i-1])); x < left_x; ++x) { const point p(x, i+top); rgb_alpha_pixel temp = alpha_pixel; temp.alpha = static_cast<unsigned char>((x-left_boundary[i-1])/std::abs(delta)*max_alpha); if (valid_area.contains(p)) assign_pixel(c[p.y()-c.top()][p.x()-c.left()],temp); } } else // on the top side { const long old_left_x = static_cast<long>(std::ceil(left_boundary[i-1])); for (long x = left_x; x < old_left_x; ++x) { const point p(x, i+top-1); rgb_alpha_pixel temp = alpha_pixel; temp.alpha = static_cast<unsigned char>((x-left_boundary[i])/delta*max_alpha); if (valid_area.contains(p)) assign_pixel(c[p.y()-c.top()][p.x()-c.left()],temp); } } // Alpha blend the edges on the right side delta = right_boundary[i-1] - right_boundary[i]; if (std::abs(delta) <= 1) { if (std::ceil(right_boundary[i]) != right_x) { const point p(static_cast<long>(std::ceil(right_boundary[i])), i+top); rgb_alpha_pixel temp = alpha_pixel; temp.alpha = max_alpha-static_cast<unsigned char>((p.x()-right_boundary[i])*max_alpha); if (valid_area.contains(p)) assign_pixel(c[p.y()-c.top()][p.x()-c.left()],temp); } } else if (delta < 0) // on the top side { for (long x = static_cast<long>(std::floor(right_boundary[i-1]))+1; x <= right_x; ++x) { const point p(x, i+top-1); rgb_alpha_pixel temp = alpha_pixel; temp.alpha = static_cast<unsigned char>((right_boundary[i]-x)/std::abs(delta)*max_alpha); if (valid_area.contains(p)) assign_pixel(c[p.y()-c.top()][p.x()-c.left()],temp); } } else // on the bottom side { const long old_right_x = static_cast<long>(std::floor(right_boundary[i-1])); for (long x = right_x+1; x <= old_right_x; ++x) { const point p(x, i+top); rgb_alpha_pixel temp = alpha_pixel; temp.alpha = static_cast<unsigned char>((right_boundary[i-1]-x)/delta*max_alpha); if (valid_area.contains(p)) assign_pixel(c[p.y()-c.top()][p.x()-c.left()],temp); } } } } inline void draw_solid_convex_polygon ( const canvas& c, const std::vector<point>& polygon ) { draw_solid_convex_polygon(c, polygon, 0); } // ---------------------------------------------------------------------------------------- template < typename image_type > void draw_image ( const canvas& c, const point& p, const image_type& img, const rectangle& area_ = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { const long x = p.x(); const long y = p.y(); rectangle rect(x,y,img.nc()+x-1,img.nr()+y-1); rectangle area = c.intersect(rect).intersect(area_); if (area.is_empty()) return; for (long row = area.top(); row <= area.bottom(); ++row) { for (long col = area.left(); col <= area.right(); ++col) { assign_pixel(c[row-c.top()][col-c.left()], img[row-rect.top()][col-rect.left()]); } } } // ---------------------------------------------------------------------------------------- template < typename image_type > void draw_image ( const canvas& c, const rectangle& rect, const image_type& img, const rectangle& area_ = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { const rectangle area = c.intersect(rect).intersect(area_); if (area.is_empty() || img.size() == 0) return; const matrix<long,1> x = matrix_cast<long>(round(linspace(0, img.nc()-1, rect.width()))); const matrix<long,1> y = matrix_cast<long>(round(linspace(0, img.nr()-1, rect.height()))); for (long row = area.top(); row <= area.bottom(); ++row) { const long r = y(row-rect.top()); long cc = area.left() - rect.left(); for (long col = area.left(); col <= area.right(); ++col) { assign_pixel(c[row-c.top()][col-c.left()], img[r][x(cc++)]); } } } // ---------------------------------------------------------------------------------------- template <typename pixel_type> void draw_rounded_rectangle ( const canvas& c, const rectangle& rect, unsigned radius, const pixel_type& color, const rectangle& area_ = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { if ( rect.intersect ( c ).is_empty() ) return; draw_line ( c, point(rect.left() + radius + 1, rect.bottom()), point(rect.right() - radius - 1, rect.bottom()), color,area_ ); draw_line ( c, point(rect.left() + radius + 1, rect.top()), point(rect.right() - radius - 1, rect.top()), color,area_ ); draw_line ( c, point(rect.left(), rect.top() + radius + 1), point(rect.left(), rect.bottom() - radius - 1), color,area_ ); draw_line ( c, point(rect.right(), rect.top() + radius + 1), point(rect.right(), rect.bottom() - radius - 1), color,area_ ); unsigned x = radius, y = 0, old_x = x; point p; while ( x > y ) { p = point(rect.left() + radius - y, rect.top() + radius - x); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.right() - radius + y, rect.top() + radius - x); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.right() - radius + y, rect.bottom() - radius + x); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.left() + radius - y, rect.bottom() - radius + x); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.left() + radius - x, rect.top() + radius - y); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.right() - radius + x, rect.top() + radius - y); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.right() - radius + x, rect.bottom() - radius + y); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.left() + radius - x, rect.bottom() - radius + y); if (area_.contains(p)) draw_pixel (c, p , color ); y++; old_x = x; x = square_root ( ( radius * radius - y * y ) * 4 ) / 2; } if ( x == y && old_x != x ) { p = point(rect.left() + radius - y, rect.top() + radius - x); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.right() - radius + y, rect.top() + radius - x); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.right() - radius + y, rect.bottom() - radius + x); if (area_.contains(p)) draw_pixel (c, p , color ); p = point(rect.left() + radius - y, rect.bottom() - radius + x); if (area_.contains(p)) draw_pixel (c, p , color ); } } // ---------------------------------------------------------------------------------------- template <typename pixel_type> void fill_gradient_rounded ( const canvas& c, const rectangle& rect, unsigned long radius, const pixel_type& top_color, const pixel_type& bottom_color, const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { rectangle valid_area(c.intersect(area.intersect(rect))); if ( valid_area.is_empty() ) return; unsigned long m_prev = 0, m = radius, c_div = valid_area.height() - 1; const long c_top = valid_area.top(); const long c_bottom = valid_area.bottom(); for ( long y = c_top; y <= c_bottom;y++ ) { unsigned long c_s = y - c_top; unsigned long c_t = c_bottom - y; if ( c_div == 0 ) { // only a single round, just take the average color c_div = 2; c_s = c_t = 1; } rgb_alpha_pixel color; vector_to_pixel(color, ((pixel_to_vector<unsigned long>(top_color)*c_t + pixel_to_vector<unsigned long>(bottom_color)*c_s)/c_div)); unsigned long s = y - rect.top(); unsigned long t = rect.bottom() - y; if ( s < radius ) { m = radius - square_root ( ( radius * radius - ( radius - s ) * ( radius - s ) ) * 4 ) / 2; if ( s == m && m + 1 < m_prev ) // these are hacks to remove distracting artefacts at small radii m++; } else if ( t < radius ) { m = radius - square_root ( ( radius * radius - ( radius - t ) * ( radius - t ) ) * 4 ) / 2; if ( t == m && m == m_prev ) m++; } else { m = 0; } m_prev = m; draw_line ( c, point(rect.left() + m, y), point(rect.right() - m, y), color, valid_area ); } } // ---------------------------------------------------------------------------------------- template <typename pixel_type> void draw_rectangle ( const canvas& c, rectangle rect, const pixel_type& pixel, const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { // top line draw_line(c, point(rect.left(),rect.top()), point(rect.right(),rect.top()), pixel, area); // bottom line draw_line(c, point(rect.left(),rect.bottom()), point(rect.right(),rect.bottom()), pixel, area); // left line draw_line(c, point(rect.left(),rect.top()), point(rect.left(),rect.bottom()), pixel, area); // right line draw_line(c, point(rect.right(),rect.top()), point(rect.right(),rect.bottom()), pixel, area); } inline void draw_rectangle ( const canvas& c, rectangle rect ){ draw_rectangle(c, rect, 0); } // ---------------------------------------------------------------------------------------- template <typename pixel_type> void fill_rect ( const canvas& c, const rectangle& rect, const pixel_type& pixel ) { rectangle area = rect.intersect(c); for (long y = area.top(); y <= area.bottom(); ++y) { for (long x = area.left(); x <= area.right(); ++x) { assign_pixel(c[y-c.top()][x-c.left()], pixel); } } } // ---------------------------------------------------------------------------------------- template <typename pixel_type> void fill_rect_with_vertical_gradient ( const canvas& c, const rectangle& rect, const pixel_type& pixel_top, const pixel_type& pixel_bottom, const rectangle& area_ = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(), std::numeric_limits<long>::max(), std::numeric_limits<long>::max()) ) { rectangle area = rect.intersect(c).intersect(area_); pixel_type pixel; const long s = rect.bottom()-rect.top(); for (long y = area.top(); y <= area.bottom(); ++y) { const long t = rect.bottom()-y; const long b = y-rect.top(); vector_to_pixel(pixel, ((pixel_to_vector<long>(pixel_top)*t + pixel_to_vector<long>(pixel_bottom)*b)/s)); for (long x = area.left(); x <= area.right(); ++x) { assign_pixel(c[y-c.top()][x-c.left()], pixel); } } } // ---------------------------------------------------------------------------------------- } #ifdef NO_MAKEFILE #include "canvas_drawing.cpp" #endif #endif // DLIB_GUI_CANVAS_DRAWINg_