Merge commit 'origin/master' into gallium-0.2
[mesa.git] / progs / glsl / convolutions.c
1 /**
2 * Convolution with GLSL.
3 * Note: uses GL_ARB_shader_objects, GL_ARB_vertex_shader, GL_ARB_fragment_shader,
4 * not the OpenGL 2.0 shader API.
5 * Author: Zack Rusin
6 */
7
8 #define GL_GLEXT_PROTOTYPES
9 #include "readtex.h"
10
11 #include <GL/glut.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <assert.h>
15 #include <math.h>
16
17 enum Filter {
18 GAUSSIAN_BLUR,
19 SHARPEN,
20 MEAN_REMOVAL,
21 EMBOSS,
22 EDGE_DETECT,
23 NO_FILTER,
24 LAST
25 };
26 #define QUIT LAST
27
28 struct BoundingBox {
29 float minx, miny, minz;
30 float maxx, maxy, maxz;
31 };
32 struct Texture {
33 GLuint id;
34 GLfloat x;
35 GLfloat y;
36 GLint width;
37 GLint height;
38 GLenum format;
39 };
40
41 static const char *textureLocation = "../images/girl2.rgb";
42
43 static GLfloat viewRotx = 0.0, viewRoty = 0.0, viewRotz = 0.0;
44 static struct BoundingBox box;
45 static struct Texture texture;
46 static GLuint program;
47 static GLint menuId;
48 static enum Filter filter = GAUSSIAN_BLUR;
49
50
51 static void checkError(int line)
52 {
53 GLenum err = glGetError();
54 if (err) {
55 printf("GL Error %s (0x%x) at line %d\n",
56 gluErrorString(err), (int) err, line);
57 }
58 }
59
60 static void loadAndCompileShader(GLuint shader, const char *text)
61 {
62 GLint stat;
63
64 glShaderSource(shader, 1, (const GLchar **) &text, NULL);
65
66 glCompileShader(shader);
67
68 glGetShaderiv(shader, GL_COMPILE_STATUS, &stat);
69 if (!stat) {
70 GLchar log[1000];
71 GLsizei len;
72 glGetShaderInfoLog(shader, 1000, &len, log);
73 fprintf(stderr, "Problem compiling shader: %s\n", log);
74 exit(1);
75 }
76 else {
77 printf("Shader compiled OK\n");
78 }
79 }
80
81 static void readShader(GLuint shader, const char *filename)
82 {
83 const int max = 100*1000;
84 int n;
85 char *buffer = (char*) malloc(max);
86 FILE *f = fopen(filename, "r");
87 if (!f) {
88 fprintf(stderr, "Unable to open shader file %s\n", filename);
89 exit(1);
90 }
91
92 n = fread(buffer, 1, max, f);
93 printf("Read %d bytes from shader file %s\n", n, filename);
94 if (n > 0) {
95 buffer[n] = 0;
96 loadAndCompileShader(shader, buffer);
97 }
98
99 fclose(f);
100 free(buffer);
101 }
102
103
104 static void
105 checkLink(GLuint prog)
106 {
107 GLint stat;
108 glGetProgramiv(prog, GL_LINK_STATUS, &stat);
109 if (!stat) {
110 GLchar log[1000];
111 GLsizei len;
112 glGetProgramInfoLog(prog, 1000, &len, log);
113 fprintf(stderr, "Linker error:\n%s\n", log);
114 }
115 else {
116 fprintf(stderr, "Link success!\n");
117 }
118 }
119
120 static void fillConvolution(GLint *k,
121 GLfloat *scale,
122 GLfloat *color)
123 {
124 switch(filter) {
125 case GAUSSIAN_BLUR:
126 k[0] = 1; k[1] = 2; k[2] = 1;
127 k[3] = 2; k[4] = 4; k[5] = 2;
128 k[6] = 1; k[7] = 2; k[8] = 1;
129
130 *scale = 1./16.;
131 break;
132 case SHARPEN:
133 k[0] = 0; k[1] = -2; k[2] = 0;
134 k[3] = -2; k[4] = 11; k[5] = -2;
135 k[6] = 0; k[7] = -2; k[8] = 0;
136
137 *scale = 1./3.;
138 break;
139 case MEAN_REMOVAL:
140 k[0] = -1; k[1] = -1; k[2] = -1;
141 k[3] = -1; k[4] = 9; k[5] = -1;
142 k[6] = -1; k[7] = -1; k[8] = -1;
143
144 *scale = 1./1.;
145 break;
146 case EMBOSS:
147 k[0] = -1; k[1] = 0; k[2] = -1;
148 k[3] = 0; k[4] = 4; k[5] = 0;
149 k[6] = -1; k[7] = 0; k[8] = -1;
150
151 *scale = 1./1.;
152 color[0] = 0.5;
153 color[1] = 0.5;
154 color[2] = 0.5;
155 color[3] = 0.5;
156 break;
157 case EDGE_DETECT:
158 k[0] = 1; k[1] = 1; k[2] = 1;
159 k[3] = 0; k[4] = 0; k[5] = 0;
160 k[6] = -1; k[7] = -1; k[8] = -1;
161
162 *scale = 1.;
163 color[0] = 0.5;
164 color[1] = 0.5;
165 color[2] = 0.5;
166 color[3] = 0.5;
167 break;
168 case NO_FILTER:
169 k[0] = 0; k[1] = 0; k[2] = 0;
170 k[3] = 0; k[4] = 1; k[5] = 0;
171 k[6] = 0; k[7] = 0; k[8] = 0;
172
173 *scale = 1.;
174 break;
175 default:
176 assert(!"Unhandled switch value");
177 }
178 }
179
180 static void setupConvolution()
181 {
182 GLint *kernel = (GLint*)malloc(sizeof(GLint) * 9);
183 GLfloat scale;
184 GLfloat *vecKer = (GLfloat*)malloc(sizeof(GLfloat) * 9 * 4);
185 GLuint loc;
186 GLuint i;
187 GLfloat baseColor[4];
188 baseColor[0] = 0;
189 baseColor[1] = 0;
190 baseColor[2] = 0;
191 baseColor[3] = 0;
192
193 fillConvolution(kernel, &scale, baseColor);
194 /*vector of 4*/
195 for (i = 0; i < 9; ++i) {
196 vecKer[i*4 + 0] = kernel[i];
197 vecKer[i*4 + 1] = kernel[i];
198 vecKer[i*4 + 2] = kernel[i];
199 vecKer[i*4 + 3] = kernel[i];
200 }
201
202 loc = glGetUniformLocationARB(program, "KernelValue");
203 glUniform4fv(loc, 9, vecKer);
204 loc = glGetUniformLocationARB(program, "ScaleFactor");
205 glUniform4f(loc, scale, scale, scale, scale);
206 loc = glGetUniformLocationARB(program, "BaseColor");
207 glUniform4f(loc, baseColor[0], baseColor[1],
208 baseColor[2], baseColor[3]);
209
210 free(vecKer);
211 free(kernel);
212 }
213
214 static void createProgram(const char *vertProgFile,
215 const char *fragProgFile)
216 {
217 GLuint fragShader = 0, vertShader = 0;
218
219 program = glCreateProgram();
220 if (vertProgFile) {
221 vertShader = glCreateShader(GL_VERTEX_SHADER);
222 readShader(vertShader, vertProgFile);
223 glAttachShader(program, vertShader);
224 }
225
226 if (fragProgFile) {
227 fragShader = glCreateShader(GL_FRAGMENT_SHADER);
228 readShader(fragShader, fragProgFile);
229 glAttachShader(program, fragShader);
230 }
231
232 glLinkProgram(program);
233 checkLink(program);
234
235 glUseProgram(program);
236
237 /*
238 assert(glIsProgram(program));
239 assert(glIsShader(fragShader));
240 assert(glIsShader(vertShader));
241 */
242
243 checkError(__LINE__);
244 {/*texture*/
245 GLuint texLoc = glGetUniformLocationARB(program, "srcTex");
246 glUniform1iARB(texLoc, 0);
247 }
248 {/*setup offsets */
249 float offsets[] = { 1.0 / texture.width, 1.0 / texture.height,
250 0.0 , 1.0 / texture.height,
251 -1.0 / texture.width, 1.0 / texture.height,
252 1.0 / texture.width, 0.0,
253 0.0 , 0.0,
254 -1.0 / texture.width, 0.0,
255 1.0 / texture.width, -1.0 / texture.height,
256 0.0 , -1.0 / texture.height,
257 -1.0 / texture.width, -1.0 / texture.height };
258 GLuint offsetLoc = glGetUniformLocationARB(program, "Offset");
259 glUniform2fv(offsetLoc, 9, offsets);
260 }
261 setupConvolution();
262
263 checkError(__LINE__);
264 }
265
266
267 static void readTexture(const char *filename)
268 {
269 GLubyte *data;
270
271 texture.x = 0;
272 texture.y = 0;
273
274 glGenTextures(1, &texture.id);
275 glBindTexture(GL_TEXTURE_2D, texture.id);
276 glTexParameteri(GL_TEXTURE_2D,
277 GL_TEXTURE_MIN_FILTER, GL_NEAREST);
278 glTexParameteri(GL_TEXTURE_2D,
279 GL_TEXTURE_MAG_FILTER, GL_NEAREST);
280 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
281 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
282 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
283 data = LoadRGBImage(filename, &texture.width, &texture.height,
284 &texture.format);
285 if (!data) {
286 printf("Error: couldn't load texture image '%s'\n", filename);
287 exit(1);
288 }
289 printf("Texture %s (%d x %d)\n",
290 filename, texture.width, texture.height);
291 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
292 texture.width, texture.height, 0, texture.format,
293 GL_UNSIGNED_BYTE, data);
294 }
295
296 static void menuSelected(int entry)
297 {
298 switch (entry) {
299 case QUIT:
300 exit(0);
301 break;
302 default:
303 filter = (enum Filter)entry;
304 }
305 setupConvolution();
306
307 glutPostRedisplay();
308 }
309
310 static void menuInit()
311 {
312 menuId = glutCreateMenu(menuSelected);
313
314 glutAddMenuEntry("Gaussian blur", GAUSSIAN_BLUR);
315 glutAddMenuEntry("Sharpen", SHARPEN);
316 glutAddMenuEntry("Mean removal", MEAN_REMOVAL);
317 glutAddMenuEntry("Emboss", EMBOSS);
318 glutAddMenuEntry("Edge detect", EDGE_DETECT);
319 glutAddMenuEntry("None", NO_FILTER);
320
321 glutAddMenuEntry("Quit", QUIT);
322
323 glutAttachMenu(GLUT_RIGHT_BUTTON);
324 }
325
326 static void init()
327 {
328 if (!glutExtensionSupported("GL_ARB_shader_objects") ||
329 !glutExtensionSupported("GL_ARB_vertex_shader") ||
330 !glutExtensionSupported("GL_ARB_fragment_shader")) {
331 fprintf(stderr, "Sorry, this program requires GL_ARB_shader_objects, GL_ARB_vertex_shader, and GL_ARB_fragment_shader\n");
332 exit(1);
333 }
334
335 fprintf(stderr, "GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
336 fprintf(stderr, "GL_VERSION = %s\n", (char *) glGetString(GL_VERSION));
337 fprintf(stderr, "GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR));
338
339 menuInit();
340 readTexture(textureLocation);
341 createProgram("convolution.vert", "convolution.frag");
342
343 glEnable(GL_TEXTURE_2D);
344 glClearColor(1.0, 1.0, 1.0, 1.0);
345 /*glShadeModel(GL_SMOOTH);*/
346 glShadeModel(GL_FLAT);
347 }
348
349 static void reshape(int width, int height)
350 {
351 glViewport(0, 0, width, height);
352 glMatrixMode(GL_PROJECTION);
353 glLoadIdentity();
354 box.minx = 0;
355 box.maxx = width;
356 box.miny = 0;
357 box.maxy = height;
358 box.minz = 0;
359 box.maxz = 1;
360 glOrtho(box.minx, box.maxx, box.miny, box.maxy, -999999, 999999);
361 glMatrixMode(GL_MODELVIEW);
362 }
363
364 static void keyPress(unsigned char key, int x, int y)
365 {
366 switch(key) {
367 case 27:
368 exit(0);
369 default:
370 return;
371 }
372 glutPostRedisplay();
373 }
374
375 static void
376 special(int k, int x, int y)
377 {
378 switch (k) {
379 case GLUT_KEY_UP:
380 viewRotx += 2.0;
381 break;
382 case GLUT_KEY_DOWN:
383 viewRotx -= 2.0;
384 break;
385 case GLUT_KEY_LEFT:
386 viewRoty += 2.0;
387 break;
388 case GLUT_KEY_RIGHT:
389 viewRoty -= 2.0;
390 break;
391 default:
392 return;
393 }
394 glutPostRedisplay();
395 }
396
397
398 static void draw()
399 {
400 GLfloat center[2];
401 GLfloat anchor[2];
402
403 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
404
405 glLoadIdentity();
406 glPushMatrix();
407
408 center[0] = box.maxx/2;
409 center[1] = box.maxy/2;
410 anchor[0] = center[0] - texture.width/2;
411 anchor[1] = center[1] - texture.height/2;
412
413 glTranslatef(center[0], center[1], 0);
414 glRotatef(viewRotx, 1.0, 0.0, 0.0);
415 glRotatef(viewRoty, 0.0, 1.0, 0.0);
416 glRotatef(viewRotz, 0.0, 0.0, 1.0);
417 glTranslatef(-center[0], -center[1], 0);
418
419 glTranslatef(anchor[0], anchor[1], 0);
420 glBegin(GL_TRIANGLE_STRIP);
421 {
422 glColor3f(1., 0., 0.);
423 glTexCoord2f(0, 0);
424 glVertex3f(0, 0, 0);
425
426 glColor3f(0., 1., 0.);
427 glTexCoord2f(0, 1.0);
428 glVertex3f(0, texture.height, 0);
429
430 glColor3f(1., 0., 0.);
431 glTexCoord2f(1.0, 0);
432 glVertex3f(texture.width, 0, 0);
433
434 glColor3f(0., 1., 0.);
435 glTexCoord2f(1, 1);
436 glVertex3f(texture.width, texture.height, 0);
437 }
438 glEnd();
439
440 glPopMatrix();
441
442 glFlush();
443
444 glutSwapBuffers();
445 }
446
447 int main(int argc, char **argv)
448 {
449 glutInit(&argc, argv);
450
451 glutInitWindowPosition(0, 0);
452 glutInitWindowSize(400, 400);
453 glutInitDisplayMode(GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE);
454
455 if (!glutCreateWindow("Image Convolutions")) {
456 fprintf(stderr, "Couldn't create window!\n");
457 exit(1);
458 }
459
460 init();
461
462 glutReshapeFunc(reshape);
463 glutKeyboardFunc(keyPress);
464 glutSpecialFunc(special);
465 glutDisplayFunc(draw);
466
467
468 glutMainLoop();
469 return 0;
470 }