nuclear@0: #include nuclear@0: #include nuclear@1: #include nuclear@1: #include nuclear@1: #ifdef __APPLE__ nuclear@1: #include nuclear@1: #else nuclear@0: #include nuclear@1: #endif nuclear@0: nuclear@0: #define MAX_POS 512 nuclear@0: nuclear@0: typedef struct key { nuclear@0: int pos; nuclear@0: float val; nuclear@0: struct key *next; nuclear@0: } key_t; nuclear@0: nuclear@0: struct track { nuclear@0: key_t *keys; nuclear@0: int count; nuclear@0: }; nuclear@0: nuclear@0: void redraw(void); nuclear@0: void draw_curves(void); nuclear@0: void draw_grad(void); nuclear@0: nuclear@0: void set_val(struct track *track, float pos, float val); nuclear@0: void rm_val(struct track *track, float pos); nuclear@0: float get_val(struct track *track, float pos); nuclear@0: nuclear@1: int save_ppm(const char *fname, int img_width); nuclear@1: int save_grad(const char *fname); nuclear@0: nuclear@0: void key_handler(unsigned char key, int x, int y); nuclear@0: void skey_handler(int key, int x, int y); nuclear@0: void mbutton_handler(int bn, int state, int x, int y); nuclear@0: void drag_handler(int x, int y); nuclear@0: void reshape_handler(int x, int y); nuclear@0: nuclear@0: int view_xsz = 640; nuclear@0: int view_ysz = 480; nuclear@0: nuclear@0: struct track tred, tgreen, tblue; nuclear@0: nuclear@0: int main(int argc, char **argv) nuclear@0: { nuclear@0: glutInit(&argc, argv); nuclear@0: glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); nuclear@0: glutInitWindowSize(view_xsz, view_ysz); nuclear@0: glutCreateWindow("Mindlapse gradiant editor"); nuclear@0: nuclear@0: glutDisplayFunc(redraw); nuclear@0: glutKeyboardFunc(key_handler); nuclear@0: glutSpecialFunc(skey_handler); nuclear@0: glutMouseFunc(mbutton_handler); nuclear@0: glutMotionFunc(drag_handler); nuclear@0: glutReshapeFunc(reshape_handler); nuclear@0: nuclear@0: glClearColor(0, 0, 0, 0); nuclear@0: glEnable(GL_POINT_SMOOTH); nuclear@0: nuclear@0: glMatrixMode(GL_PROJECTION); nuclear@0: glOrtho(0, 1, 0, 1, -1, 1); nuclear@0: glMatrixMode(GL_MODELVIEW); nuclear@0: nuclear@0: tred.keys = malloc(sizeof *tred.keys); nuclear@0: tred.keys->next = 0; nuclear@0: nuclear@0: tgreen.keys = malloc(sizeof *tgreen.keys); nuclear@0: tgreen.keys->next = 0; nuclear@0: nuclear@0: tblue.keys = malloc(sizeof *tblue.keys); nuclear@0: tblue.keys->next = 0; nuclear@0: nuclear@0: glutMainLoop(); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void redraw(void) nuclear@0: { nuclear@0: glClear(GL_COLOR_BUFFER_BIT); nuclear@0: nuclear@0: draw_curves(); nuclear@0: nuclear@0: draw_grad(); nuclear@0: nuclear@0: glutSwapBuffers(); nuclear@0: } nuclear@0: nuclear@0: void draw_curves(void) nuclear@0: { nuclear@0: int i; nuclear@0: key_t *ptr; nuclear@0: nuclear@0: glLoadIdentity(); nuclear@0: glTranslatef(0, 0.25, 0); nuclear@0: glScalef(1, 0.75, 1); nuclear@0: nuclear@0: glBegin(GL_LINES); nuclear@0: /* draw grid */ nuclear@0: glColor3f(0.5, 0.5, 0.5); nuclear@0: glVertex2f(0, 0); glVertex2f(0, 1); nuclear@0: glVertex2f(1, 0); glVertex2f(1, 1); nuclear@0: glVertex2f(0, 0); glVertex2f(1, 0); nuclear@0: glVertex2f(0, 1); glVertex2f(1, 1); nuclear@0: nuclear@0: glVertex2f(0, 0.25); glVertex2f(1, 0.25); nuclear@0: glVertex2f(0, 0.5); glVertex2f(1, 0.5); nuclear@0: glVertex2f(0, 0.75); glVertex2f(1, 0.75); nuclear@0: nuclear@0: glVertex2f(0.25, 0); glVertex2f(0.25, 1); nuclear@0: glVertex2f(0.5, 0); glVertex2f(0.5, 1); nuclear@0: glVertex2f(0.75, 0); glVertex2f(0.75, 1); nuclear@0: glEnd(); nuclear@0: nuclear@0: glEnable(GL_BLEND); nuclear@0: glBlendFunc(GL_ONE, GL_ONE); nuclear@0: nuclear@0: glBegin(GL_LINES); nuclear@0: for(i=0; i<3; i++) { nuclear@0: glColor3f(i == 0, i == 1, i == 2); nuclear@0: ptr = (i == 0 ? tred.keys->next : (i == 1 ? tgreen.keys->next : tblue.keys->next)); nuclear@0: nuclear@0: if(ptr && ptr->pos > 0) { nuclear@0: glVertex2f(0, ptr->val); nuclear@0: glVertex2f(ptr->pos / (float)MAX_POS, ptr->val); nuclear@0: } nuclear@0: nuclear@0: while(ptr && ptr->next) { nuclear@0: glVertex2f(ptr->pos / (float)MAX_POS, ptr->val); nuclear@0: glVertex2f(ptr->next->pos / (float)MAX_POS, ptr->next->val); nuclear@0: ptr = ptr->next; nuclear@0: } nuclear@0: nuclear@0: if(ptr && ptr->pos != MAX_POS) { nuclear@0: glVertex2f(ptr->pos / (float)MAX_POS, ptr->val); nuclear@0: glVertex2f(1, ptr->val); nuclear@0: } nuclear@0: } nuclear@0: glEnd(); nuclear@0: nuclear@0: glPointSize(5); nuclear@0: glBegin(GL_POINTS); nuclear@0: for(i=0; i<3; i++) { nuclear@0: glColor3f(i == 0, i == 1, i == 2); nuclear@0: ptr = (i == 0 ? tred.keys->next : (i == 1 ? tgreen.keys->next : tblue.keys->next)); nuclear@0: nuclear@0: while(ptr) { nuclear@0: glVertex2f(ptr->pos / (float)MAX_POS, ptr->val); nuclear@0: ptr = ptr->next; nuclear@0: } nuclear@0: } nuclear@0: glEnd(); nuclear@0: nuclear@0: glDisable(GL_BLEND); nuclear@0: } nuclear@0: nuclear@0: #define GRAD_SAMPLES MAX_POS nuclear@0: void draw_grad(void) nuclear@0: { nuclear@0: int i; nuclear@0: nuclear@0: glLoadIdentity(); nuclear@0: glScalef(1, 0.25, 1); nuclear@0: nuclear@0: glBegin(GL_QUADS); nuclear@0: for(i=0; ikeys; nuclear@0: int ipos = (int)(pos * MAX_POS); nuclear@0: nuclear@0: if(!ptr->next || ptr->next->pos > ipos) { nuclear@0: nkey = malloc(sizeof *nkey); nuclear@0: nkey->pos = ipos; nuclear@0: nkey->val = val; nuclear@0: nkey->next = ptr->next; nuclear@0: ptr->next = nkey; nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: while(ptr->next && ptr->next->pos < ipos) { nuclear@0: ptr = ptr->next; nuclear@0: } nuclear@0: nuclear@0: if(ptr->pos == ipos) { nuclear@0: ptr->val = val; nuclear@0: } else { nuclear@0: nkey = malloc(sizeof *nkey); nuclear@0: nkey->pos = ipos; nuclear@0: nkey->val = val; nuclear@0: nkey->next = ptr->next; nuclear@0: ptr->next = nkey; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void rm_val(struct track *tr, float pos) nuclear@0: { nuclear@0: key_t *ptr = tr->keys; nuclear@0: int ipos = (int)(pos * MAX_POS); nuclear@0: nuclear@0: while(ptr->next && ptr->next->pos <= ipos) { nuclear@0: if(ptr->next->pos == ipos) { nuclear@0: key_t *tmp = ptr->next; nuclear@0: ptr->next = ptr->next->next; nuclear@0: free(tmp); nuclear@0: return; nuclear@0: } nuclear@0: ptr = ptr->next; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: float get_val(struct track *tr, float pos) nuclear@0: { nuclear@0: float t; nuclear@0: key_t *ptr = tr->keys->next; nuclear@0: int ipos = (int)(pos * MAX_POS); nuclear@0: nuclear@0: if(!ptr) { nuclear@0: return 0.0; nuclear@0: } nuclear@0: nuclear@0: while(ptr && ptr->pos <= ipos) { nuclear@0: if(ptr->pos == ipos || !ptr->next) { nuclear@0: return ptr->val; nuclear@0: } nuclear@0: nuclear@0: if(ptr->next->pos > ipos) { nuclear@0: t = (float)(ipos - ptr->pos) / (float)(ptr->next->pos - ptr->pos); nuclear@0: return ptr->val + (ptr->next->val - ptr->val) * t; nuclear@0: } nuclear@0: nuclear@0: ptr = ptr->next; nuclear@0: } nuclear@0: nuclear@0: return tr->keys->next->val; nuclear@0: } nuclear@0: nuclear@0: void clear_track(struct track *tr) nuclear@0: { nuclear@0: key_t *ptr = tr->keys->next; nuclear@0: while(ptr) { nuclear@0: key_t *tmp = ptr; nuclear@0: ptr = ptr->next; nuclear@0: free(tmp); nuclear@0: } nuclear@0: tr->keys->next = 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@1: int save_ppm(const char *fname, int img_width) nuclear@0: { nuclear@0: int i; nuclear@0: FILE *fp; nuclear@0: nuclear@1: if(!(fp = fopen(fname, "wb"))) { nuclear@1: fprintf(stderr, "failed to write %s: %s\n", fname, strerror(errno)); nuclear@1: return -1; nuclear@0: } nuclear@0: nuclear@1: fprintf(fp, "P6\n%d 1\n255\n", img_width); nuclear@0: nuclear@1: for(i=0; ikeys->next; nuclear@2: nuclear@1: while(ptr) { nuclear@1: float t = (float)ptr->pos / (float)MAX_POS; nuclear@1: float r = get_val(&tred, t); nuclear@1: float g = get_val(&tgreen, t); nuclear@1: float b = get_val(&tblue, t); nuclear@1: nuclear@1: fprintf(fp, "%f\t%f %f %f\n", t, r, g, b); nuclear@1: ptr = ptr->next; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: fclose(fp); nuclear@1: return 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void key_handler(unsigned char key, int x, int y) nuclear@0: { nuclear@0: skey_handler(key, x, y); nuclear@0: } nuclear@0: nuclear@0: void skey_handler(int key, int x, int y) nuclear@0: { nuclear@0: switch(key) { nuclear@0: case 27: nuclear@0: exit(0); nuclear@0: nuclear@0: case 's': nuclear@0: case 'S': nuclear@1: save_ppm("grad.ppm", 256); nuclear@1: break; nuclear@1: nuclear@1: case 'g': nuclear@1: case 'G': nuclear@1: save_grad("grad.grad"); nuclear@0: break; nuclear@0: nuclear@0: case 'c': nuclear@0: clear_track(&tred); nuclear@0: clear_track(&tgreen); nuclear@0: clear_track(&tblue); nuclear@0: glutPostRedisplay(); nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static int px, py; nuclear@0: static float ppos, pval; nuclear@0: static struct track *tr; nuclear@0: nuclear@0: void mbutton_handler(int bn, int state, int x, int y) nuclear@0: { nuclear@0: float pos, val; nuclear@0: nuclear@0: pos = (float)x / (float)view_xsz; nuclear@0: val = 1.0 - (float)y / (float)(view_ysz - view_ysz / 4.0); nuclear@0: nuclear@0: if(bn == GLUT_LEFT_BUTTON) { nuclear@0: tr = &tred; nuclear@0: } else if(bn == GLUT_MIDDLE_BUTTON) { nuclear@0: tr = &tgreen; nuclear@0: } else if(bn == GLUT_RIGHT_BUTTON) { nuclear@0: tr = &tblue; nuclear@0: } else { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if(glutGetModifiers() & GLUT_ACTIVE_CTRL) { nuclear@0: tr = 0; nuclear@0: } nuclear@0: nuclear@0: if(state == 0) { nuclear@0: px = x; nuclear@0: py = y; nuclear@0: ppos = pos; nuclear@0: pval = val; nuclear@0: } else { nuclear@0: if(px == -1 || abs(x - px) > 2 || abs(y - py) > 2) { nuclear@0: return; nuclear@0: } nuclear@0: if(val < 0.0 || val > 1.0) return; nuclear@0: nuclear@0: nuclear@0: if(glutGetModifiers() & GLUT_ACTIVE_SHIFT) { nuclear@0: if(tr) { nuclear@0: rm_val(tr, pos); nuclear@0: } else { nuclear@0: rm_val(&tred, pos); nuclear@0: rm_val(&tgreen, pos); nuclear@0: rm_val(&tblue, pos); nuclear@0: } nuclear@0: } else { nuclear@0: if(tr) { nuclear@0: set_val(tr, pos, val); nuclear@0: } else { nuclear@0: set_val(&tred, pos, val); nuclear@0: set_val(&tgreen, pos, val); nuclear@0: set_val(&tblue, pos, val); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: px = -1; nuclear@0: py = -1; nuclear@0: glutPostRedisplay(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void drag_handler(int x, int y) nuclear@0: { nuclear@0: float pos = (float)x / (float)view_xsz; nuclear@0: float val = 1.0 - (float)y / (float)(view_ysz - view_ysz / 4.0); nuclear@0: nuclear@0: if(pos < 0.0) pos = 0.0; nuclear@0: if(pos > 1.0) pos = 1.0; nuclear@0: if(val < 0.0) val = 0.0; nuclear@0: if(val > 1.0) val = 1.0; nuclear@0: nuclear@0: if(tr) { nuclear@0: rm_val(tr, ppos); nuclear@0: rm_val(tr, ppos - 1.0 / (float)view_xsz); nuclear@0: rm_val(tr, ppos + 1.0 / (float)view_xsz); nuclear@0: set_val(tr, pos, val); nuclear@0: } else { nuclear@0: rm_val(&tred, ppos); nuclear@0: rm_val(&tgreen, ppos); nuclear@0: rm_val(&tblue, ppos); nuclear@0: rm_val(&tred, ppos - 1.0 / (float)view_xsz); nuclear@0: rm_val(&tgreen, ppos - 1.0 / (float)view_xsz); nuclear@0: rm_val(&tblue, ppos - 1.0 / (float)view_xsz); nuclear@0: rm_val(&tred, ppos + 1.0 / (float)view_xsz); nuclear@0: rm_val(&tgreen, ppos + 1.0 / (float)view_xsz); nuclear@0: rm_val(&tblue, ppos + 1.0 / (float)view_xsz); nuclear@0: set_val(&tred, pos, val); nuclear@0: set_val(&tgreen, pos, val); nuclear@0: set_val(&tblue, pos, val); nuclear@0: } nuclear@0: nuclear@0: ppos = pos; nuclear@0: pval = val; nuclear@0: nuclear@0: glutPostRedisplay(); nuclear@0: } nuclear@0: nuclear@0: void reshape_handler(int x, int y) nuclear@0: { nuclear@0: view_xsz = x; nuclear@0: view_ysz = y; nuclear@0: nuclear@0: glViewport(0, 0, x, y); nuclear@0: }