|
|
|
|
|
|
|
#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 (x1 <= valid_area.right() && x1 >= valid_area.left()) |
|
{ |
|
|
|
if (y1 > y2) |
|
swap(y1,y2); |
|
|
|
y1 = std::max(y1,valid_area.top()); |
|
y2 = std::min(y2,valid_area.bottom()); |
|
|
|
for (long y = y1; y <= y2; ++y) |
|
{ |
|
assign_pixel(c[y-c.top()][x1-c.left()], pixel); |
|
} |
|
} |
|
} |
|
else if (y1 == y2) |
|
{ |
|
|
|
if (y1 <= valid_area.bottom() && y1 >= valid_area.top()) |
|
{ |
|
|
|
if (x1 > x2) |
|
swap(x1,x2); |
|
|
|
x1 = std::max(x1,valid_area.left()); |
|
x2 = std::min(x2,valid_area.right()); |
|
|
|
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; |
|
|
|
|
|
|
|
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; |
|
|
|
|
|
long middle = std::min(x-1,last_x); |
|
for (long i = first_x; i <= middle; ++i) |
|
{ |
|
double a = i - x + 0.5; |
|
|
|
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; |
|
|
|
for (long i = last_x; i >= middle; --i) |
|
{ |
|
double a = i - x - 0.5; |
|
|
|
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; |
|
|
|
|
|
|
|
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; |
|
|
|
|
|
long middle = std::min(x-1,last_x); |
|
for (long i = first_x; i <= middle; ++i) |
|
{ |
|
double a = i - x + 0.5; |
|
|
|
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; |
|
|
|
for (long i = last_x; i >= middle; --i) |
|
{ |
|
double a = i - x - 0.5; |
|
|
|
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 |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
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()); |
|
|
|
|
|
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 |
|
{ |
|
|
|
|
|
|
|
const double m = (p2.x() - p1.x())/(double)(p2.y()-p1.y()); |
|
const double b = p1.x() - m*p1.y(); |
|
|
|
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]; |
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
long top = max(valid_area.top(),bounding_box.top()); |
|
long bottom = min(valid_area.bottom(),bounding_box.bottom()); |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
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) |
|
{ |
|
|
|
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; |
|
|
|
|
|
|
|
|
|
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) |
|
{ |
|
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 |
|
{ |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
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) |
|
{ |
|
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 |
|
{ |
|
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,num_columns(img)+x-1,num_rows(img)+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() || num_columns(img) * num_rows(img) == 0) |
|
return; |
|
|
|
const matrix<long,1> x = matrix_cast<long>(round(linspace(0, num_columns(img)-1, rect.width()))); |
|
const matrix<long,1> y = matrix_cast<long>(round(linspace(0, num_rows(img)-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 ) |
|
{ |
|
|
|
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 ) |
|
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()) |
|
) |
|
{ |
|
|
|
draw_line(c, point(rect.left(),rect.top()), |
|
point(rect.right(),rect.top()), |
|
pixel, area); |
|
|
|
|
|
draw_line(c, point(rect.left(),rect.bottom()), |
|
point(rect.right(),rect.bottom()), |
|
pixel, area); |
|
|
|
|
|
draw_line(c, point(rect.left(),rect.top()), |
|
point(rect.left(),rect.bottom()), |
|
pixel, area); |
|
|
|
|
|
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 |
|
|
|
|