// try4.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#include <gl/glut.h>
#include <math.h>
//#include <vld.h>
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();
}