// bmpview.c // // Copyright 2010 Edward V. Emelianoff // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, // MA 02110-1301, USA. //-lglut #include "imageview.h" #include "macros.h" #include #include // XInitThreads(); int totWindows = 0; // total number of opened windows pthread_t GLUTthread; // main GLUT thread pthread_mutex_t winini_mutex = PTHREAD_MUTEX_INITIALIZER; // mutex for windows initialization volatile int wannacreate = 0; // flag: ==1 if someone wants to create window windowData *wininiptr = NULL; int initialized = 0; // ==1 if GLUT is initialized; ==0 after clear_GL_context int wannakill_GL_ID = 0; // GL_ID of window for asynchroneous killing void createWindow(windowData *win); void RedrawWindow(); void Resize(int width, int height); /** * calculate window properties on creating & resizing */ void calc_win_props(windowData *win, GLfloat *Wortho, GLfloat *Hortho){ if(!win || ! win->image) return; double a, A, w, h, W, H; double Zoom = win->zoom; w = (double)win->image->w / 2.; h = (double)win->image->h / 2.; W = (double)win->w; H = (double)win->h; A = W / H; a = w / h; if(A > a){ // now W & H are parameters for glOrtho win->Daspect = h / H * 2.; W = h * A; H = h; }else{ win->Daspect = w / W * 2.; H = w / A; W = w; } if(Wortho) *Wortho = W; if(Hortho) *Hortho = H; // calculate coordinates of center win->x0 = W/Zoom - w + win->x / Zoom; win->y0 = H / Zoom + h - win->y / Zoom; } /** * create window & run main loop */ void createWindow(windowData *win){ FNAME(); if(!initialized) return; if(!win) return; int w = win->w, h = win->h; DBG("create window with title %s", win->title); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(w, h); win->GL_ID = glutCreateWindow(win->title); DBG("created GL_ID=%d", win->GL_ID); glutReshapeFunc(Resize); glutDisplayFunc(RedrawWindow); glutKeyboardFunc(keyPressed); glutSpecialFunc(keySpPressed); //glutMouseWheelFunc(mouseWheel); glutMouseFunc(mousePressed); glutMotionFunc(mouseMove); //glutIdleFunc(glutPostRedisplay); glutIdleFunc(NULL); DBG("init textures"); glGenTextures(1, &(win->Tex)); calc_win_props(win, NULL, NULL); win->zoom = 1. / win->Daspect; glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, win->Tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, win->image->w, win->image->h, 0, GL_RGB, GL_UNSIGNED_BYTE, win->image->rawdata); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glDisable(GL_TEXTURE_2D); totWindows++; createMenu(win->GL_ID); DBG("OK, total opened windows: %d", totWindows); } int killwindow(int GL_ID){ DBG("try to kill win GL_ID=%d", GL_ID); windowData *win; win = searchWindow_byGLID(GL_ID); if(!win) return 0; glutSetWindow(GL_ID); // obviously set window (for closing from menu) if(!win->killthread){ pthread_mutex_lock(&win->mutex); // say changed thread to die win->killthread = 1; pthread_mutex_unlock(&win->mutex); DBG("wait for changed thread"); pthread_join(win->thread, NULL); // wait while thread dies } if(win->menu) glutDestroyMenu(win->menu); glutDestroyWindow(win->GL_ID); win->GL_ID = 0; // reset for forEachWindow() DBG("destroy texture %d", win->Tex); glDeleteTextures(1, &(win->Tex)); glFinish(); if(!removeWindow(win->ID)) WARNX(_("Error removing from list")); totWindows--; return 1; } /** * destroy window with OpenGL or inner identificator "window" * @param window inner or OpenGL id * @param idtype = INNER or OPENGL * @return 1 in case of OK, 0 if fault */ int destroyWindow(int window, winIdType type){ if(!initialized) return 0; int r = 0; DBG("lock"); pthread_mutex_lock(&winini_mutex); DBG("locked"); if(type == INNER){ windowData *win = searchWindow(window); if(win) r = killwindow(win->GL_ID); }else r = killwindow(window); pthread_mutex_unlock(&winini_mutex); DBG("window killed"); return r; } /** * asynchroneous destroying - for using from menu */ void destroyWindow_async(int window_GL_ID){ pthread_mutex_lock(&winini_mutex); wannakill_GL_ID = window_GL_ID; pthread_mutex_unlock(&winini_mutex); } void renderBitmapString(float x, float y, void *font, char *string, GLubyte *color){ if(!initialized) return; char *c; int x1=x, W=0; for(c = string; *c; c++){ W += glutBitmapWidth(font,*c);// + 1; } x1 -= W/2; glColor3ubv(color); glLoadIdentity(); glTranslatef(0.,0., -150); //glTranslatef(x,y, -4000.); for (c = string; *c != '\0'; c++){ glColor3ubv(color); glRasterPos2f(x1,y); glutBitmapCharacter(font, *c); //glutStrokeCharacter(GLUT_STROKE_ROMAN, *c); x1 = x1 + glutBitmapWidth(font,*c);// + 1; } } void redisplay(int GL_ID){ if(!initialized) return; glutSetWindow(GL_ID); glutPostRedisplay(); } /* if(redraw) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_LUMINANCE, GL_FLOAT, ptro); // s/image->data/tex/ else glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY, w, h, 0, GL_LUMINANCE, GL_FLOAT, ptro); //glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); //glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); */ void RedrawWindow(){ if(!initialized) return; int window; window = glutGetWindow(); windowData *win = searchWindow_byGLID(window); if(!win) return; if(pthread_mutex_trylock(&win->mutex) != 0) return; int w = win->image->w, h = win->image->h; glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); //glTranslatef(win->x-w/2., win->y-h/2., win->z); glTranslatef(win->x, win->y, 0.); glScalef(-win->zoom, -win->zoom, 1.); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, win->Tex); if(win->image->changed){ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, win->image->rawdata); win->image->changed = 0; } /* glBegin(GL_QUADS); glTexCoord2f(0., 1.); glVertex3f(0., 0., 0.); glTexCoord2f(0., 0.); glVertex3f(0.,h, 0.); glTexCoord2f(1., 0.); glVertex3f(w, h, 0.); glTexCoord2f(1., 1.); glVertex3f(w, 0., 0.); glEnd(); */ w /= 2.; h /= 2.; glBegin(GL_QUADS); /* glTexCoord2f(1.0f, 1.0f); glVertex2f( w, h ); // top right glTexCoord2f(1.0f, 0.0f); glVertex2f( w, -h ); // bottom right glTexCoord2f(0.0f, 0.0f); glVertex2f(-w, -h ); // bottom left glTexCoord2f(0.0f, 1.0f); glVertex2f(-w, h ); // top left */ glTexCoord2f(1.0f, 1.0f); glVertex2f( -w, -h ); // top right glTexCoord2f(1.0f, 0.0f); glVertex2f( -w, h ); // bottom right glTexCoord2f(0.0f, 0.0f); glVertex2f(w, h ); // bottom left glTexCoord2f(0.0f, 1.0f); glVertex2f(w, -h ); // top left glEnd(); glDisable(GL_TEXTURE_2D); glFinish(); glutSwapBuffers(); pthread_mutex_unlock(&win->mutex); } /** * main freeGLUT loop * waits for global signals to create windows & make other actions */ void *Redraw(_U_ void *arg){ // pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER; // struct timeval tv; // struct timespec timeToWait; // struct timeval now; while(1){ pthread_mutex_lock(&winini_mutex); if(!initialized){ DBG("!initialized"); pthread_mutex_unlock(&winini_mutex); pthread_exit(NULL); } if(wannacreate){ // someone asks to create window DBG("call for window creating, id: %d", wininiptr->ID); createWindow(wininiptr); DBG("done!"); wininiptr = NULL; wannacreate = 0; } if(wannakill_GL_ID){ usleep(10000); // wait a little to be sure that caller is closed killwindow(wannakill_GL_ID); wannakill_GL_ID = 0; } forEachWindow(redisplay); /* gettimeofday(&now,NULL); timeToWait.tv_sec = now.tv_sec; timeToWait.tv_nsec = now.tv_usec * 1000UL + 10000000UL; pthread_cond_timedwait(&fakeCond, &winini_mutex, &timeToWait);*/ pthread_mutex_unlock(&winini_mutex); //pthread_testcancel(); if(totWindows) glutMainLoopEvent(); // process actions if there are windows /* gettimeofday(&now,NULL); timeToWait.tv_sec = now.tv_sec; timeToWait.tv_nsec = now.tv_usec * 1000UL + 10000000UL; pthread_mutex_lock(&fakeMutex); pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait); pthread_mutex_unlock(&fakeMutex);*/ /* tv.tv_sec = 0; tv.tv_usec = 10000; select(0, NULL, NULL, NULL, &tv);*/ usleep(10000); } return NULL; } void Resize(int width, int height){ if(!initialized) return; int window = glutGetWindow(); windowData *win = searchWindow_byGLID(window); if(!win) return; /* int GRAB_WIDTH = win->w, GRAB_HEIGHT = win->h; float _U_ tmp, wd = (float) width/GRAB_WIDTH, ht = (float)height/GRAB_HEIGHT; tmp = (wd + ht) / 2.; width = (int)(tmp * GRAB_WIDTH); height = (int)(tmp * GRAB_HEIGHT);*/ glutReshapeWindow(width, height); win->w = width; win->h = height; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); //GLfloat W = (GLfloat)GRAB_WIDTH; GLfloat H = (GLfloat)GRAB_HEIGHT; GLfloat W, H; calc_win_props(win, &W, &H); glOrtho(-W,W, -H,H, -1., 1.); // gluPerspective(90., W/H, 0., 100.0); // gluLookAt(0., 0., H/2., 0., 0., 0., 0., 1., 0.); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /** * create new window, run thread & return pointer to its structure or NULL * asynchroneous call from outside * wait for window creating & return its data * @param title - header (copyed inside this function) * @param w,h - image size * @param rawdata - NULL (then the memory will be allocated here with size w x h) * or allocated outside data */ windowData *createGLwin(char *title, int w, int h, rawimage *rawdata){ FNAME(); if(!initialized) return NULL; windowData *win = MALLOC(windowData, 1); if(!addWindow(win)){ FREE(win); return NULL; } rawimage *raw; if(rawdata){ raw = rawdata; }else{ raw = MALLOC(rawimage, 1); if(raw){ raw->rawdata = MALLOC(GLubyte, w*h*3); raw->w = w; raw->h = h; raw->changed = 1; // raw->protected is zero automatically } } if(!raw || !raw->rawdata){ free(raw); return NULL; } win->title = strdup(title); win->image = raw; if(pthread_mutex_init(&win->mutex, NULL)){ WARN(_("Can't init mutex!")); removeWindow(win->ID); return NULL; } win->w = w; win->h = h; while(wannacreate); // wait if there was another creating pthread_mutex_lock(&winini_mutex); wininiptr = win; wannacreate = 1; pthread_mutex_unlock(&winini_mutex); DBG("wait for creatin"); while(wannacreate); // wait until window created from main thread DBG("window created"); return win; } /** * Init freeGLUT */ void imageview_init(){ FNAME(); char *v[] = {PROJNAME, NULL}; int c = 1; static int glutnotinited = 1; if(initialized){ // "Уже инициализировано!" WARNX(_("Already initialized!")); return; } if(glutnotinited){ XInitThreads(); // we need it for threaded windows glutInit(&c, v); glutnotinited = 0; } glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); pthread_create(&GLUTthread, NULL, &Redraw, NULL); initialized = 1; } void killwindow_v(int GL_ID){ DBG("GL_ID: %d", GL_ID); killwindow(GL_ID); } /** * Close all opened windows and terminate main GLUT thread */ void clear_GL_context(){ FNAME(); if(!initialized) return; DBG("lock"); pthread_mutex_lock(&winini_mutex); initialized = 0; DBG("locked"); // kill main GLUT thread // pthread_cancel(GLUTthread); pthread_mutex_unlock(&winini_mutex); forEachWindow(killwindow_v); DBG("join"); pthread_join(GLUTthread, NULL); // wait while main thread exits // pthread_mutex_unlock(&winini_mutex); DBG("main GL thread cancelled"); } /* * Coordinates transformation from CS of drawingArea into CS of picture * x,y - pointer coordinates * X,Y - coordinates of appropriate point at picture */ void conv_mouse_to_image_coords(int x, int y, float *X, float *Y, windowData *window){ float a = window->Daspect / window->zoom; *X = x * a - window->x0; *Y = window->y0 - y * a; } void conv_image_to_mouse_coords(float X, float Y, int *x, int *y, windowData *window){ float a = window->zoom / window->Daspect; *x = (X + window->x0) * a; *y = (window->y0 - Y) * a; }