/* grTOGL3.c - * * Copyright 2003 Open Circuit Design, Inc., for MultiGiG Ltd. * * This file contains additional functions to manipulate an X window system * color display. Included here are device-dependent routines to draw and * erase text and draw a grid. * */ #include #include #include "tcltk/tclmagic.h" #include "utils/magic.h" #include "utils/geometry.h" #include "utils/malloc.h" #include "windows/windows.h" #include "graphics/graphics.h" #include "graphics/graphicsInt.h" #include "dbwind/dbwind.h" #include "textio/textio.h" #include "utils/signals.h" #include "utils/utils.h" #include "utils/hash.h" #include "graphics/grTOGLInt.h" #include "graphics/grTkCommon.h" #include "database/fonts.h" /* C99 compat * GL headers must be included after graphics/grTOGLInt.h */ #include #include #include #include extern Display *grXdpy; /* locals */ GLuint grXBases[4]; typedef struct { GLuint framebuffer; GLuint renderbuffer; } RenderFrame; /*--------------------------------------------------------- * grtoglDrawGrid: * grxDrawGrid adds a grid to the grid layer, using the current * write mask and color. * * Results: * TRUE is returned normally. However, if the grid gets too small * to be useful, then nothing is drawn and FALSE is returned. * * Side Effects: None. *--------------------------------------------------------- */ bool grtoglDrawGrid (prect, outline, clip) Rect *prect; /* A rectangle that forms the template * for the grid. Note: in order to maintain * precision for the grid, the rectangle * coordinates are specified in units of * screen coordinates multiplied by SUBPIXEL. */ int outline; /* the outline style */ Rect *clip; /* a clipping rectangle */ { int xsize, ysize; int x, y; int xstart, ystart; int snum, low, hi, shifted; xsize = prect->r_xtop - prect->r_xbot; ysize = prect->r_ytop - prect->r_ybot; if (!xsize || !ysize || GRID_TOO_SMALL(xsize, ysize)) return FALSE; xstart = prect->r_xbot % xsize; while (xstart < clip->r_xbot << SUBPIXELBITS) xstart += xsize; ystart = prect->r_ybot % ysize; while (ystart < clip->r_ybot << SUBPIXELBITS) ystart += ysize; grtoglSetLineStyle(outline); glBegin(GL_LINES); snum = 0; low = clip->r_ybot; hi = clip->r_ytop; for (x = xstart; x < (clip->r_xtop+1) << SUBPIXELBITS; x += xsize) { shifted = x >> SUBPIXELBITS; glVertex2i(shifted, low); glVertex2i(shifted, hi); snum++; } snum = 0; low = clip->r_xbot; hi = clip->r_xtop; for (y = ystart; y < (clip->r_ytop+1) << SUBPIXELBITS; y += ysize) { shifted = y >> SUBPIXELBITS; glVertex2i(low, shifted); glVertex2i(hi, shifted); snum++; } glEnd(); return TRUE; } /*--------------------------------------------------------- * grtoglLoadFont * This local routine transfers the X font bitmaps * into OpenGL display lists for simple text * rendering. * * Results: Success/Failure * * Side Effects: None. *--------------------------------------------------------- */ bool grtoglLoadFont() { Font id; unsigned int i; for (i = 0; i < 4; i++) { id = Tk_FontId(grTkFonts[i]); grXBases[i] = glGenLists(256); if (grXBases[i] == 0) { TxError("Out of display lists!\n"); return FALSE; } glXUseXFont(id, 0, 256, grXBases[i]); } return TRUE; } /*--------------------------------------------------------- * grtoglSetCharSize: * This local routine sets the character size in the display, * if necessary. * * Results: None. * * Side Effects: None. *--------------------------------------------------------- */ void grtoglSetCharSize (size) int size; /* Width of characters, in pixels (6 or 8). */ { toglCurrent.fontSize = size; switch (size) { case GR_TEXT_DEFAULT: case GR_TEXT_SMALL: toglCurrent.font = grSmallFont; break; case GR_TEXT_MEDIUM: toglCurrent.font = grMediumFont; break; case GR_TEXT_LARGE: toglCurrent.font = grLargeFont; break; case GR_TEXT_XLARGE: toglCurrent.font = grXLargeFont; break; default: TxError("%s%d\n", "grtoglSetCharSize: Unknown character size ", size ); break; } } /* * ---------------------------------------------------------------------------- * GrTOGLTextSize -- * * Determine the size of a text string. * * Results: * Returns 0 when 'r' updated, otherwise -1 on error (no side-effects). * * Side effects: * A rectangle is filled in that is the size of the text in pixels. * The origin (0, 0) of this rectangle is located on the baseline * at the far left side of the string. * ---------------------------------------------------------------------------- */ int GrTOGLTextSize(text, size, r) char *text; int size; Rect *r; { Tk_FontMetrics overall; Tk_Font font; int width; switch (size) { case GR_TEXT_DEFAULT: case GR_TEXT_SMALL: font = grSmallFont; break; case GR_TEXT_MEDIUM: font = grMediumFont; break; case GR_TEXT_LARGE: font = grLargeFont; break; case GR_TEXT_XLARGE: font = grXLargeFont; break; default: font = NULL; TxError("%s%d\n", "GrTOGLTextSize: Unknown character size ", size ); break; } if (font == NULL) return -1; Tk_GetFontMetrics(font, &overall); width = Tk_TextWidth(font, text, strlen(text)); /* Hack alert! Tk_TextWidth returns values too small! */ width = width + (width >> 4); r->r_ytop = overall.ascent; r->r_ybot = -overall.descent; r->r_xtop = width; r->r_xbot = 0; return 0; } /* OpenGL backing store functions (now removed from the X11-based ones) */ /* Since we always paint into the front buffer, the back buffer is */ /* always available for backing store. We need not create or destroy */ /* it. We just use the w_backingStore location to store whether the */ /* backing store contains valid data or not. */ void grtoglFreeBackingStore(MagWindow *w) { RenderFrame *rf; rf = (RenderFrame *)w->w_backingStore; if (rf == NULL) return; glDeleteFramebuffers(1, &rf->framebuffer); glDeleteRenderbuffers(1, &rf->renderbuffer); freeMagic(w->w_backingStore); w->w_backingStore = (ClientData)0; } void grtoglCreateBackingStore(MagWindow *w) { RenderFrame *rf; Tk_Window tkwind = (Tk_Window)w->w_grdata; unsigned int width, height; /* ignore all windows other than layout */ if (w->w_client != DBWclientID) return; /* Deferred */ if (tkwind == NULL) return; width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot; height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot; rf = (RenderFrame *)w->w_backingStore; if (rf != (ClientData)NULL) { glDeleteFramebuffers(1, &rf->framebuffer); glDeleteRenderbuffers(1, &rf->renderbuffer); } else { rf = (RenderFrame *)mallocMagic(sizeof(RenderFrame)); w->w_backingStore = (ClientData)rf; } glGenFramebuffers(1, &rf->framebuffer); glGenRenderbuffers(1, &rf->renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, rf->renderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB, width, height); } bool grtoglGetBackingStore(MagWindow *w, Rect *area) { RenderFrame *rf; unsigned int width, height; int xbot, ybot; Rect r; if (w->w_backingStore == (ClientData)0) return FALSE; GEO_EXPAND(area, 1, &r); GeoClip(&r, &(w->w_screenArea)); width = r.r_xtop - r.r_xbot; height = r.r_ytop - r.r_ybot; xbot = r.r_xbot; ybot = r.r_ybot; rf = (RenderFrame *)w->w_backingStore; glBindFramebuffer(GL_READ_FRAMEBUFFER, rf->framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rf->renderbuffer); glDrawBuffer(GL_FRONT); glReadBuffer(GL_COLOR_ATTACHMENT0); glBlitFramebuffer(r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop, r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); return TRUE; } bool grtoglScrollBackingStore(MagWindow *w, Point *shift) { RenderFrame *rf; GLuint FramebufferName, RenderbufferName; unsigned int width, height; int xorigin, yorigin, xshift, yshift; if (w->w_backingStore == (ClientData)0) { fprintf(stdout, "grtoglScrollBackingStore %d %d failure\n", shift->p_x, shift->p_y); return FALSE; } width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot; height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot; xorigin = 0; yorigin = 0; xshift = shift->p_x; yshift = shift->p_y; if (xshift > 0) width -= xshift; else if (xshift < 0) { width += xshift; xorigin = -xshift; xshift = 0; } if (yshift > 0) height -= yshift; else if (yshift < 0) { height += yshift; yorigin = -yshift; yshift = 0; } rf = (RenderFrame *)w->w_backingStore; glBindFramebuffer(GL_READ_FRAMEBUFFER, rf->framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rf->renderbuffer); glBlitFramebuffer(xorigin, yorigin, xorigin + width, yorigin + height, xshift, yshift, xshift + width, yshift + height, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rf->framebuffer); glBlitFramebuffer(xshift, yshift, xshift + width, yshift + height, xshift, yshift, xshift + width, yshift + height, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); return TRUE; } void grtoglPutBackingStore(MagWindow *w, Rect *area) { RenderFrame *rf; GLuint FramebufferName, RenderbufferName; unsigned int width, height; int ybot, xbot; Rect r; if (w->w_backingStore == (ClientData)0) return; if (w->w_flags & WIND_OBSCURED) { grtoglFreeBackingStore(w); return; } GEO_EXPAND(area, 1, &r); GeoClip(&r, &(w->w_screenArea)); width = r.r_xtop - r.r_xbot; height = r.r_ytop - r.r_ybot; ybot = r.r_ybot; xbot = r.r_xbot; rf = (RenderFrame *)w->w_backingStore; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rf->framebuffer); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rf->renderbuffer); glDrawBuffer(GL_COLOR_ATTACHMENT0); glReadBuffer(GL_FRONT); glBlitFramebuffer(r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop, r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } /* * ---------------------------------------------------------------------------- * GrTOGLReadPixel -- * * Read one pixel from the screen. * * Results: * An integer containing the pixel's color. * * Side effects: * none. * * ---------------------------------------------------------------------------- */ int GrTOGLReadPixel (w, x, y) MagWindow *w; int x,y; /* the location of a pixel in screen coords */ { return 0; /* OpenGL has no such function, so return 0 */ } /* * ---------------------------------------------------------------------------- * GrTOGLBitBlt -- * * Copy information in bit block transfers. * * Results: * None. * * Side effects: * changes the screen. * ---------------------------------------------------------------------------- */ void GrTOGLBitBlt(r, p) Rect *r; Point *p; { glCopyPixels(r->r_xbot, r->r_ybot, r->r_xtop - r->r_xbot + 1, r->r_ytop - r->r_ybot + 1, GL_COLOR); } #ifdef VECTOR_FONTS /* *---------------------------------------------------------------------- * * Technically, there should be no self-intersecting polygons in outline * fonts. However, decomposition of bezier curves into line segments * may occasionally produce one, so it needs to be handled. *---------------------------------------------------------------------- */ void myCombine(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4], GLdouble **outData, void *dataptr) { /* This needs to be free'd at the end of gluTessEndPolygon()! */ GLdouble *new = (GLdouble *)mallocMagic(2 * sizeof(GLdouble)); new[0] = coords[0]; new[1] = coords[1]; *outData = new; /* Diagnostic */ TxError("Intersecting polygon in char \"%c\" at %g %g!\n", *((char *)dataptr), coords[0], coords[1]); } /* *---------------------------------------------------------------------- * Draw a text character * This routine differs from grtoglFillPolygon() in that it uses the * glu library to handle non-convex polygons as may appear in font * outlines. *---------------------------------------------------------------------- */ void grtoglDrawCharacter(clist, tc, pixsize) FontChar *clist; unsigned char tc; int pixsize; { Point *tp; int np, nptotal; int i, j; static GLUtesselator *tess = NULL; static GLdouble *v = NULL; static int maxnp = 0; FontChar *ccur; if (pixsize < 5) return; /* Label too small to be useful */ if (tess == NULL) { tess = gluNewTess(); gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)glBegin); gluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)glVertex3dv); gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr)glEnd); gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)myCombine); } // Boundary-only does not look particularly good. . . gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE); nptotal = 0; for (ccur = clist; ccur != NULL; ccur = ccur->fc_next) nptotal += ccur->fc_numpoints; if (nptotal > maxnp) { if (v != NULL) freeMagic((char *)v); maxnp = nptotal; v = (GLdouble *)mallocMagic(nptotal * 3 * sizeof(GLdouble)); } j = 0; for (ccur = clist; ccur != NULL; ccur = ccur->fc_next) { tp = ccur->fc_points; np = ccur->fc_numpoints; for (i = 0; i < np; i++, j += 3) { v[j] = tp[i].p_x; v[j + 1] = tp[i].p_y; v[j + 2] = 0; } } gluTessBeginPolygon(tess, (GLvoid *)(&tc)); j = 0; for (ccur = clist; ccur != NULL; ccur = ccur->fc_next) { np = ccur->fc_numpoints; gluTessBeginContour(tess); for (i = 0; i < np; i++, j += 3) { gluTessVertex(tess, &v[j], &v[j]); } gluTessEndContour(tess); } gluTessEndPolygon(tess); } /*--------------------------------------------------------- * grtoglFontText: * * This routine draws text from font vectors using the * font vector routines in DBlabel.c. Text is clipped * to the clipping rectangle. * * For speed, we should be transferring the font * vectors into OpenGL display lists! * *--------------------------------------------------------- */ void grtoglFontText(text, font, size, rotate, pos, clip, obscure) char *text; /* The text to be drawn */ int font; /* Font to use from fontList */ int size; /* Pixel size of the font */ int rotate; /* Text rotation */ Point *pos; /* Text base position */ Rect *clip; /* Clipping area */ LinkedRect *obscure; /* List of obscuring areas */ { char *tptr; Point *coffset; /* vector to next character */ Rect *cbbox; GLfloat fsize, matvals[16]; FontChar *clist; int cheight, baseline; float tmp; /* Keep it simple for now---ignore clip and obscure */ glDisable(GL_BLEND); glPushMatrix(); glTranslated(pos->p_x, pos->p_y, 0); glRotated(rotate, 0, 0, 1); /* Get label size */ cbbox = &DBFontList[font]->mf_extents; fsize = (GLfloat)size / (GLfloat)cbbox->r_ytop; glScalef(fsize, fsize, 1.0); /* Adjust to baseline */ baseline = 0; for (tptr = text; *tptr != '\0'; tptr++) { DBFontChar(font, *tptr, NULL, NULL, &cbbox); if (cbbox->r_ybot < baseline) baseline = cbbox->r_ybot; } glTranslated(0, -baseline, 0); for (tptr = text; *tptr != '\0'; tptr++) { DBFontChar(font, *tptr, &clist, &coffset, NULL); grtoglDrawCharacter(clist, *tptr, size); glTranslated(coffset->p_x, coffset->p_y, 0); } glPopMatrix(); } #endif /* VECTOR_FONTS */ /*--------------------------------------------------------- * grtoglPutText: * (modified on SunPutText) * * This routine puts a chunk of text on the screen in the current * color, size, etc. The caller must ensure that it fits on * the screen -- no clipping is done except to the obscuring rectangle * list and the clip rectangle. * * Results: * none. * * Side Effects: * The text is drawn on the screen. * *--------------------------------------------------------- */ void grtoglPutText (text, pos, clip, obscure) char *text; /* The text to be drawn. */ Point *pos; /* A point located at the leftmost point of * the baseline for this string. */ Rect *clip; /* A rectangle to clip against */ LinkedRect *obscure; /* A list of obscuring rectangles */ { Rect location; Rect overlap; Rect textrect; LinkedRect *ob; void grTOGLGeoSub(); int i; float tscale; if (GrTOGLTextSize(text, toglCurrent.fontSize, &textrect) < 0) return; location.r_xbot = pos->p_x + textrect.r_xbot; location.r_xtop = pos->p_x + textrect.r_xtop; location.r_ybot = pos->p_y + textrect.r_ybot; location.r_ytop = pos->p_y + textrect.r_ytop; /* erase parts of the bitmap that are obscured */ for (ob = obscure; ob != NULL; ob = ob->r_next) { if (GEO_TOUCH(&ob->r_r, &location)) { overlap = location; GeoClip(&overlap, &ob->r_r); grTOGLGeoSub(&location, &overlap); } } overlap = location; GeoClip(&overlap, clip); /* copy the text to the color screen */ if ((overlap.r_xbot < overlap.r_xtop)&&(overlap.r_ybot <= overlap.r_ytop)) { glScissor(overlap.r_xbot, overlap.r_ybot, overlap.r_xtop - overlap.r_xbot, overlap.r_ytop - overlap.r_ybot); glEnable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glRasterPos2i(pos->p_x, pos->p_y); glListBase(grXBases[(toglCurrent.fontSize == GR_TEXT_DEFAULT) ? GR_TEXT_SMALL : toglCurrent.fontSize]); glCallLists(strlen(text), GL_UNSIGNED_BYTE, (unsigned char *)text); glDisable(GL_SCISSOR_TEST); } } /* grTOGLGeoSub: * return the tallest sub-rectangle of r not obscured by area * area must be within r. */ void grTOGLGeoSub(r, area) Rect *r; /* Rectangle to be subtracted from. */ Rect *area; /* Area to be subtracted. */ { if (r->r_xbot == area->r_xbot) r->r_xbot = area->r_xtop; else if (r->r_xtop == area->r_xtop) r->r_xtop = area->r_xbot; else if (r->r_ybot <= area->r_ybot) r->r_ybot = area->r_ytop; else if (r->r_ytop == area->r_ytop) r->r_ytop = area->r_ybot; else r->r_xtop = area->r_xbot; }