// try4.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include #include #include #include //#include class UserPolygon; class Framebuffer; // Описание псевдо-глобальных переменных class Scene { public: UserPolygon *polyg; Framebuffer *buff; int screen_height, screen_width; bool smooth; ~Scene(); void initialization(); }; Scene sc; // Структра для задания цвета пикселя //#pragma pack(push, 1) struct color { unsigned char r, g, b; }; bool operator==(const color left, const color right) { return left.r == right.r && left.g == right.g && left.b == right.b; } const color operator+(const color &left, const color &right) { int c[3] = { left.r + right.r, left.g + right.g, left.b + right.b }; for (int i = 0; i < 3; i++) { if (c[i] > 255) c[i] = 255; } color sum = { c[0], c[1], c[2] }; return sum; } const color operator-(const color &left, const color &right) { int c[3] = { left.r - right.r, left.g - right.g, left.b - right.b }; for (int i = 0; i < 3; i++) { if (c[i] < 0) c[i] = 0; } color sum = { c[0], c[1], c[2] }; return sum; } color create_color(int r, int g, int b) { if (r > 255) r = 255; if (r < 0) r = 0; if (g > 255) g = 255; if (g < 0) g = 0; if (b > 255) b = 255; if (b < 0) b = 0; color c = {r, g, b}; return c; } color alpha(color c1, color c2, double p) { if (p < 0) p = 0; if (p > 1) p = 1; //unsigned char s = ceil((1-p)*255); //color sub = { s, s, s }; //if (!colf) { // printf("color r: %d, g: %d, b: %d\n", c.r, c.g, c.b); /// printf("percent p: %f\n", p); // printf("part p*g: %d\n", (char)ceil(p*c.g)); //} //colf = true; color c1_p = { ceil(p*c1.r), ceil(p*c1.g), ceil(p*c1.b) }; color c2_p = { ceil((1-p)*c2.r), ceil((1-p)*c2.g), ceil((1-p)*c2.b) }; return c1_p + c2_p; } //#pragma pack(pop) // Описание часто используемых констант const color COLOR_WHITE = {255, 255, 255}; const color COLOR_BLACK = {0, 0, 0}; const color COLOR_GREEN = {0, 200, 0}; const color COLOR_RED = {200, 0, 0}; const color COLOR_LIGHT_GREEN = {100, 255, 100}; const color NO_COLOR = { 1, 1, 1 }; // Структура точки в двухмерном пространстве struct point { int x, y; }; // Класс промежуточного буфера цвета class Framebuffer { private: color *C; // Массив цветов int w, h; // Высота и ширина буфера color clear_color; // Цвет, которым заполняется буфер при очистке color current_color; // Цвет, которым рисуются примитивы public: Framebuffer(int w, int h); // Прототип конструктора класса virtual ~Framebuffer(); // Прототип деструктора класса void increase(int w, int h); // Увеличивает рамеры буфера void set_clear_color(color); // Задает цвет, которым заполняется буфер при очистке void clear(); // Очищение буфера void set_color(color); // Задает цвет, которым рисуются примитивы void set_pixel(int x, int y, color c); // Задает пикселю (x,y) цвет c color get_pixel(int x, int y); // Возвращает цвет пикселя (x,y) void toogle_pixel(int x, int y, color on, color off); // Если цвет пикселя (x,y) не равен on, ставит on, инече - off void draw_line(int x1, int y1, int x2, int y2); // Рисует отрезок, используя целочисленный алгоритм Брезенхема void draw_line_old(int x1, int y1, int x2, int y2); // Рисует отрезок, используя целочисленный алгоритм Брезенхема void draw_line_smooth(int x1, int y1, int x2, int y2); // Алгоритм Брезенхема с устранением ступенчатости void draw_line_smooth_old(int x1, int y1, int x2, int y2); // Алгоритм Брезенхема с устранением ступенчатости void draw_polygon(point *C, int n); // Рисует многоугольник, закрашивая его по алгоритму со списком ребер и флагом void draw_loop_line(point *C, int n, bool smooth); // Рисует замкнутую ломаную void draw(); // Отображает на эране }; Framebuffer::Framebuffer(int w, int h) { this->w = w; this->h = h; C = new color[w*h]; } Framebuffer::~Framebuffer() { delete [] C; } void Framebuffer::increase(int new_w, int new_h) { if (new_w <= w) new_w = w; if (new_h <= h) new_h = h; if (new_w == w && new_h == h) return; color *new_C = new color[new_w*new_h]; for (int i = 0; i < new_w*new_h; i++) { new_C[i] = clear_color; } for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { new_C[i + j*new_w] = C[i + j*w]; } } delete [] C; w = new_w; h = new_h; C = new_C; } void Framebuffer::set_clear_color(color c) { clear_color = c; } void Framebuffer::clear() { for (int i = 0; i < w*h; i++) { C[i] = clear_color; } } void Framebuffer::set_color(color c) { current_color = c; } void Framebuffer::set_pixel(int x, int y, color c) { if (x < 0 || x >= w || y < 0 || y >= h) return; C[x + y*w] = c; } color Framebuffer::get_pixel(int x, int y) { if (x < 0 || x >= w || y < 0 || y >= h) return NO_COLOR; return C[x + y*w]; } void Framebuffer::draw_line(int x1, int y1, int x2, int y2) { color c = current_color; int dx = abs(x2 - x1); int dy = abs(y2 - y1); int dmax, dmin; if (dx > dy) { dmax = dx; dmin = dy; } else { dmax = dy; dmin = dx; } int sx = x1 < x2 ? 1 : -1; int sy = y1 < y2 ? 1 : -1; int e = 0; set_pixel(x2, y2, c); while (x1 != x2 || y1 != y2) { set_pixel(x1, y1, c); e += dmin; if (2*e > dmax) { if (dx > dy) y1 += sy; else x1 += sx; e -= dmax; } if (dx > dy) x1 += sx; else y1 += sy; } } void Framebuffer::draw_line_smooth(int x1, int y1, int x2, int y2) { color c = current_color; int dx = abs(x2 - x1); int dy = abs(y2 - y1); int dmax, dmin; if (dx > dy) { dmax = dx; dmin = dy; } else { dmax = dy; dmin = dx; } int sx = x1 < x2 ? 1 : -1; int sy = y1 < y2 ? 1 : -1; int e = 0; set_pixel(x2, y2, alpha(c, clear_color, 0.5)); double brightness = 0.5; while (x1 != x2 || y1 != y2) { set_pixel(x1, y1, alpha(c, clear_color, brightness)); e += dmin; if (2*e > dmax) { if (dx > dy) y1 += sy; else x1 += sx; e -= dmax; } if (dx > dy) x1 += sx; else y1 += sy; if (dx < dy && sy == sx || dx > dy && sy != sx) brightness = -((double)e)/dmax+0.5; else brightness = ((double)e)/dmax+0.5; } } void Framebuffer::draw_line_old(int x1, int y1, int x2, int y2) { draw_line_smooth(x1,y1,x2,y2); return; color c = current_color; int dx = abs(x2 - x1); int dy = abs(y2 - y1); int sx = x1 < x2 ? 1 : -1; int sy = y1 < y2 ? 1 : -1; // int er = dx - dy; // set_pixel(x2, y2, c); while(x1 != x2 || y1 != y2) { set_pixel(x1, y1, c); const int er2 = er * 2; // if(er2 > -dy) { er -= dy; x1 += sx; } if(er2 < dx) { er += dx; y1 += sy; } } } void Framebuffer::draw_line_smooth_old(int x1, int y1, int x2, int y2) { color c = current_color; printf("draw_line_smooth {\n"); int dx = abs(x2 - x1); int dy = abs(y2 - y1); printf(" dx = %d\n dy = %d\n\n", dx, dy); int sx = x1 < x2 ? 1 : -1; int sy = y1 < y2 ? 1 : -1; // int er = dx - dy; // set_pixel(x2, y2, c); while(x1 != x2 || y1 != y2) { const int er2 = er * 2; int x_old = x1, y_old = y1; // if(er2 > -dy) { er -= dy; x1 += sx; //set_pixel(x1, y1, create_color(5,5,5)); } if(er2 < dx) { er += dx; y1 += sy; } // printf(" er = %d, alpha = %f\n", er, (-(double)er)/dx + 1.5); set_pixel(x_old, y_old, alpha(c, clear_color, (-(double)er)/(2*dx) + 1.0)); } printf("}\n"); /*int x = x1; int y = y1; int I = 255; int dx = x2 - x1; int dy = y2 - y1; //if (!(x1 > x2 && y1 < y2 && dy < dx)) { return; } int m = (I*dy)/dx; int w = I - m; int e = 1/2; color c = current_color; set_pixel(x, y, c); while(x < x2) { if (e < w) { x = x + 1; e = e + m; } else { x++; y++; e -= w; } c.g = 255-e; c.b = 255-e; c.r = 255-e; set_pixel(x, y, c); }*/ } void Framebuffer::toogle_pixel(int x, int y, color on, color off) { if (get_pixel(x, y) == on) { set_pixel(x, y, off); } else { set_pixel(x, y, on); } } void Framebuffer::draw_loop_line(point *C, int n, bool smooth) { for (int i = 0; i < n; i++) { int j = (i + 1)%n; if (smooth) draw_line_smooth(C[i].x, C[i].y, C[j].x, C[j].y); else draw_line(C[i].x, C[i].y, C[j].x, C[j].y); } } void Framebuffer::draw_polygon(point *C, int n) { // Первый этап: рисуем границу clear(); color c = current_color; for (int i = 0; i < n; i++) { int k = (i + n - 1)%n; // k - индекс предыдущего ребра int j = (i + 1)%n; // Собираемся провести ребро от C[i] до C[j] int x1 = C[i].x, y1 = C[i].y; int x2 = C[j].x, y2 = C[j].y; // Определяем, является ли точка экстремумом bool extremum = (y1 >= y2)^(C[k].y >= y1); //if (extremum) printf("%d: extremum, C[k].y = %d\n", i, C[k].y); else printf("%d: not extremum, C[k].y = %d\n", i, C[k].y); int dx = abs(x2 - x1); int dy = abs(y2 - y1); int sx = x1 < x2 ? 1 : -1; int sy = y1 < y2 ? 1 : -1; int er = dx - dy; toogle_pixel(x2, y2, c, clear_color); if (!extremum) toogle_pixel(x1, y1, c, clear_color); // Не зажигаем пиксель на экстремумах while(x1 != x2 || y1 != y2) { const int er2 = er * 2; if(er2 > -dy) { er -= dy; x1 += sx; } if(er2 < dx) { er += dx; y1 += sy; toogle_pixel(x1, y1, c, clear_color); // Ставим пиксель только при переходе через горизонталь } } } // Второй этап: построчное сканирование bool flag = false; for (int j = 0; j < h; j++) { flag = false; for (int i = 0; i < w; i++) { if (get_pixel(i, j) == current_color) { flag = !flag; } else { if (flag) set_pixel(i, j, current_color); } } } // Третий этап: дополняем границей //draw_loop_line(C, n, false); } void Framebuffer::draw() { glDrawPixels(w, h, GL_RGB, GL_UNSIGNED_BYTE, C); glFlush(); } class UserPolygon { private: int n; // Количество вершин int capacity; // Вместимость стека вершин point *C; // Массив точек, реализующий стек вершин bool ready; // Показывает, закончено ли формирование многоугольника public: int max_x, max_y, min_x, min_y; // Координаты границ многоугольника UserPolygon(point first_point) { n = 1; capacity = 10; C = new point[capacity]; C[0] = first_point; max_x = min_x = first_point.x; max_y = min_y = first_point.y; ready = false; } virtual ~UserPolygon(); bool is_ready() { return ready; } int get_n() { return n; } void increase() { int new_capacity = capacity * 2; point *new_C = new point[new_capacity]; for (int i = 0; i < capacity; i++) { new_C[i] = C[i]; } delete [] C; C = new_C; capacity = new_capacity; } int add_vertex(point p) { if (ready) return -1; if (p.x == C[n-1].x && p.y == C[n-1].y) return -1; // Нельзя добавить две одинак. вершины подряд // Проверяем, близко ли точка к началу кривой int dx = (p.x - C[0].x); int dy = (p.y - C[0].y); int r = 10; if (dx*dx + dy*dy < r*r) { // и, если близко, то замыкаем complete(); return n+1; } C[n] = p; n++; if (p.x > max_x) max_x = p.x; if (p.x < min_x) min_x = p.x; if (p.y > max_y) max_y = p.y; if (p.y < min_y) min_y = p.y; if (n >= capacity) increase(); printf("add(%d, %d)\n", p.x, p.y); return n; } int remove_vertex() { if (ready) return -1; if (n < 1) return -1; n--; return n; } void reset() { delete [] C; capacity = 10; n = 0; C = new point[capacity]; ready = false; } int complete() { if (ready) return -1; if (n < 3) return -1; ready = true; point *new_C = new point[n]; for (int i = 0; i < n; i++) { new_C[i] = C[i]; } delete [] C; C = new_C; return n; } void draw() { //glClear(GL_COLOR_BUFFER_BIT); sc.buff->clear(); if (ready) { sc.buff->draw_polygon(C, n); } for (int i = 1; i < n; i++) { sc.buff->draw_line(C[i-1].x, C[i-1].y, C[i].x, C[i].y); } //if (ready) { // sc.buff->draw_line(C[0].x, C[0].y, C[n-1].x, C[n-1].y); //} //glFlush(); } void draw_smooth() { for (int i = 0; i < n; i++) { int j = (i + 1)%n; sc.buff->draw_line_smooth(C[i].x, C[i].y, C[j].x, C[j].y); } } void draw_aliasing() { for (int i = 0; i < n; i++) { int j = (i + 1)%n; sc.buff->draw_line(C[i].x, C[i].y, C[j].x, C[j].y); } } }; UserPolygon::~UserPolygon() { delete [] C; } void Scene::initialization() { glClearColor(1.0,1.0,1.0,0.0); glColor3f(0.0f, 0.0f, 0.0f); glPointSize(4.0); buff = new Framebuffer(screen_width, screen_height); buff->set_clear_color( COLOR_BLACK ); buff->clear(); buff->set_color( COLOR_GREEN ); } Scene::~Scene() { delete polyg; delete buff; } void display_handler() { sc.buff->draw(); } void reshape_handler(int w, int h) { w = w - w%3; sc.screen_height = h; sc.screen_width = w; sc.buff->increase(w, h); glutPostRedisplay(); } void mouse_handler(int button, int state, int x, int y) { if (x<0 || x >= sc.screen_width || y < 0 || y >= sc.screen_height) return; if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { point click_point = { x , sc.screen_height - y }; if (!sc.polyg) { printf("\nnew polyg\n"); printf("add(%d, %d)\n", click_point.x, click_point.y); sc.polyg = new UserPolygon(click_point); } else { if (sc.polyg->is_ready()) return; sc.polyg->add_vertex(click_point); sc.polyg->draw(); } } glutPostRedisplay(); } void motion_handler(int x, int y) { if (x < 0) x = 0; if (x >= sc.screen_width) x = sc.screen_width - 1; if (y < 0) y = 0; if (y >= sc.screen_height) y = sc.screen_height - 1; //if (x<0 || x >= sc.screen_width || y < 0 || y >= sc.screen_height) return; point click_point = { x , sc.screen_height - y - 1 }; if (!sc.polyg) { printf("\nnew polyg\n"); printf("add(%d, %d)\n", click_point.x, click_point.y); sc.polyg = new UserPolygon(click_point); } else { if (sc.polyg->is_ready()) return; sc.polyg->add_vertex(click_point); sc.polyg->draw(); } glutPostRedisplay(); } void draw_sample() { printf("sample"); mouse_handler(GLUT_LEFT_BUTTON, GLUT_DOWN, 480, sc.screen_height - 258); mouse_handler(GLUT_LEFT_BUTTON, GLUT_DOWN, 352, sc.screen_height - 142); mouse_handler(GLUT_LEFT_BUTTON, GLUT_DOWN, 370, sc.screen_height - 244); } void keyboard_handler(unsigned char key, int x, int y) { if (!sc.polyg) return; switch(key) { case 8: // backspace key printf("remove\n"); sc.polyg->remove_vertex(); sc.polyg->draw(); break; case ' ': // space key sc.polyg->complete(); sc.polyg->draw(); break; case ' ': // tab key printf("clear polygon\n"); delete sc.polyg; sc.polyg = 0; sc.buff->clear(); break; case '1': if (sc.polyg && sc.polyg->is_ready()) { if (sc.smooth) sc.polyg->draw_smooth(); else sc.polyg->draw_aliasing(); sc.smooth = !sc.smooth; } break; case '2': draw_sample(); break; } glutPostRedisplay(); } void main(int argc, char** argv) { sc.screen_width = 800; sc.screen_height = 400; sc.smooth = true; printf("sizeof(color) = %d\n", sizeof(color)); glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(sc.screen_width, sc.screen_height); glutInitWindowPosition(100, 150); glutCreateWindow("Rasterization"); glutMouseFunc(mouse_handler); glutMotionFunc(motion_handler); glutKeyboardFunc(keyboard_handler); glutDisplayFunc(display_handler); glutReshapeFunc(reshape_handler); sc.initialization(); glutMainLoop(); }