#include #include #include #include #include #include "texture.h" /* Some files do not define M_PI... */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef __sgi #define trunc(x) ((double)((int)(x))) #endif #define RW 0.3086 #define GW 0.6094 #define BW 0.0820 static char defaultFile0[] = "data/mandrill256.rgb"; static char defaultFile1[] = "data/weave.bw"; static char defaultBrushFile[] = "data/brush3.rgb"; GLuint *img0, *img1, *brush; GLsizei w0, w1, wbrush, h0, h1, hbrush; GLsizei w, h; GLint comp; void init(void) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } GLuint *load_img(const char *fname, GLsizei *imgW, GLsizei *imgH) { GLuint *img; img = read_texture(fname, imgW, imgH, &comp); if (!img) { fprintf(stderr, "Could not open %s\n", fname); exit(1); } return img; } GLuint * resize_img(GLuint *img, GLsizei curW, GLsizei curH) { /* save & set buffer settings */ glPushAttrib(GL_COLOR_BUFFER_BIT | GL_PIXEL_MODE_BIT); glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); glPixelZoom((float)w / (float)curW, (float)h / (float)curH); glRasterPos2i(0, 0); glDrawPixels(curW, curH, GL_RGBA, GL_UNSIGNED_BYTE, img); free(img); img = (GLuint *)malloc(w * h * sizeof(GLuint)); if (!img) { fprintf(stderr, "Malloc of %d bytes failed.\n", curW * curH * sizeof(GLuint)); exit(1); } glPixelZoom(1, 1); glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img); glPopAttrib(); return img; } GLuint * convert_to_luminance(GLuint *img, GLsizei w, GLsizei h) { GLubyte *newImg, *src, *dst; GLfloat val; int i; newImg = (GLubyte *)malloc(w * h); if (!newImg) { fprintf(stderr, "malloc of %d bytes failed\n", w*h); exit(1); } src = (GLubyte *)img; dst = newImg; for (i = 0; i < w*h; i++) { val = ((float)(*src++) * RW + (float)(*src++) * GW + (float)(*src++) * BW); src++; if (val > 255) val = 255; *dst++ = val; } free(img); /* casting a ubyte ptr to a uint pointer is sloppy since it can * lead to alignment errors, but since the pointer came from * malloc we know it's legal in this case... */ return (GLuint *)newImg; } void reshape(GLsizei winW, GLsizei winH) { glViewport(0, 0, w, h); glLoadIdentity(); glOrtho(0, winW, 0, winH, 0, 5); } void draw(void) { static int first = 1; GLenum err; if (first) { printf("Scaling images to %d by %d\n", w, h); if (w0 != w || h0 != h) { img0 = resize_img(img0, w0, h0); } if (w1 != w || h1 != h) { img1 = resize_img(img1, w1, h1); } first = 0; } glClear(GL_COLOR_BUFFER_BIT); glRasterPos2i(0, 0); glDrawBuffer(GL_BACK); glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, img1); glDrawBuffer(GL_FRONT); glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, img0); err = glGetError(); if (err != GL_NO_ERROR) printf("Error: %s\n", gluErrorString(err)); } int lastX, lastY, curX, curY; int get_msecs(void) { return glutGet(GLUT_ELAPSED_TIME) / 1000.0; } void idle(void) { int x = curX - (wbrush/2); int y = h - (curY + (hbrush/2)); int msecs; static int last_msecs = -1; /* do not do this more than 60 times a second. Otherwise it's * to fast for use on high-end systems */ msecs = get_msecs(); if (fabs(last_msecs - msecs) < 1000./60.) { return; } last_msecs = msecs; /* we draw the brush using a drawpixels command. on systems with * hardware-accelerated texture mapping it would be better to use * that. * * we use the bitmap hack to set the rasterpos because we don't * know that the position will be within the window. */ glRasterPos2i(0, 0); glBitmap(0, 0, 0, 0, x, y, 0); glColorMask(0, 0, 0, 1); glDrawBuffer(GL_BACK); glDrawPixels(wbrush, hbrush, GL_ALPHA, GL_UNSIGNED_BYTE, brush); glColorMask(1, 1, 1, 1); glReadBuffer(GL_BACK); glDrawBuffer(GL_FRONT); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glCopyPixels(x, y, wbrush, hbrush, GL_COLOR); glDisable(GL_BLEND); glColorMask(1, 1, 1, 1); } void motion(int xpos, int ypos) { curX = xpos; curY = ypos; } /* ARGSUSED */ void button(int button, int state, int xpos, int ypos) { if (state == GLUT_DOWN) { glutIdleFunc(idle); lastX = lastY = -1; curX = xpos; curY = ypos; return; } else { glutIdleFunc(0); } } /* ARGSUSED1 */ void key(unsigned char key, int x, int y) { if (key == 27) exit(0); } void show_usage(void) { fprintf(stderr, "Usage:\n"); fprintf(stderr, "paint [imagefile0] [imagefile1] [brush]\n"); } main(int argc, char *argv[]) { const char *fileName0 = defaultFile0, *fileName1 = defaultFile1, *brushName = defaultBrushFile; glutInit(&argc, argv); if (argc > 1) { fileName0 = argv[1]; } if (argc > 2) { fileName1 = argv[2]; } if (argc > 3) { brushName = argv[3]; } if (argc > 4) { show_usage(); exit(1); } printf("Image file 1 is %s\n", fileName0); printf("Image file 2 is %s\n", fileName1); printf("Brush file is %s\n", brushName); img0 = load_img(fileName0, &w0, &h0); img1 = load_img(fileName1, &w1, &h1); brush = load_img(brushName, &wbrush, &hbrush); brush = convert_to_luminance(brush, wbrush, hbrush); #define MAX(a, b) ((a) > (b) ? (a) : (b)) w = MAX(w0, w1); h = MAX(h0, h1); glutInitWindowSize(w, h); glutInitWindowPosition(0, 0); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA); glutCreateWindow(argv[0]); glutDisplayFunc(draw); glutKeyboardFunc(key); glutReshapeFunc(reshape); glutMouseFunc(button); glutMotionFunc(motion); init(); glutMainLoop(); return 0; }