/* * Image.c -- Image support routines. * * Authors : Patrick LECOANET * Creation date : Wed Dec 8 11:04:44 1999 */ /* * Copyright (c) 1999 CENA, Patrick Lecoanet -- * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This code 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this code; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include "Types.h" #include "Image.h" #include "WidgetInfo.h" #include "Geo.h" #ifdef GLX #include #include #include #include #include "Draw.h" #endif static const char rcsid[] = "$Id$"; static const char compile_id[] = "$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; /* * Key used to index the bitmap_masks hash table. */ typedef struct { Display *dpy; Pixmap bitmap; } BitmapKey; typedef struct _ImagePixmap { Display *dpy; Pixmap pixmap; Pixmap mask_pmap; struct _ImagePixmap *next; } ImagePixmap; static int image_bits_inited = 0; static Tcl_HashTable image_bits; static Tcl_HashTable bitmap_masks; #ifdef GLX static Tcl_HashTable font_textures; #endif /* ********************************************************************************** * * GetImageBits -- * ********************************************************************************** */ ImageBits * GetImageBits(ZnWindow win, char *image_name, ZnImage image) { Tcl_HashEntry *entry; XImage *im1, *im2, *mask; Pixmap pmap; Display *dpy = Tk_Display(win); int depth = Tk_Depth(win); int x, y, new, width, height; unsigned char *line; XGCValues values; GC gc; ImageBits *im_bits; ZnBool full_mask=True; if (!image_bits_inited) { Tcl_InitHashTable(&image_bits, TCL_STRING_KEYS); image_bits_inited = 1; } entry = Tcl_FindHashEntry(&image_bits, image_name); if (entry != NULL) { im_bits = (ImageBits *) Tcl_GetHashValue(entry); } else { Tk_SizeOfImage(image, &width, &height); im_bits = (ImageBits *) ZnMalloc(sizeof(ImageBits)); #ifdef GLX im_bits->texture = 0; im_bits->t_bits = NULL; #endif im_bits->pixmaps = NULL; im_bits->b_bits = NULL; pmap = XCreatePixmap(dpy, ZnWindowId(win), width, height, depth); values.foreground = 0; gc = XCreateGC(dpy, pmap, GCForeground, &values); XFillRectangle(dpy, pmap, gc, 0, 0, width, height); Tk_RedrawImage(image, 0, 0, width, height, pmap, 0, 0); im_bits->pixels = im1 = XGetImage(dpy, pmap, 0, 0, width, height, ~0L, ZPixmap); im_bits->width = width; im_bits->height = height; values.foreground = 1; XChangeGC(dpy, gc, GCForeground, &values); XFillRectangle(dpy, pmap, gc, 0, 0, width, height); XFreeGC(dpy, gc); Tk_RedrawImage(image, 0, 0, width, height, pmap, 0, 0); im2 = XGetImage(dpy, pmap, 0, 0, width, height, ~0L, ZPixmap); XFreePixmap(dpy, pmap); /* * The image structure can be setup locally (TODO). */ pmap = XCreatePixmap(dpy, ZnWindowId(win), width, height, 1); values.foreground = 0; gc = XCreateGC(dpy, pmap, GCForeground, &values); XFillRectangle(dpy, pmap, gc, 0, 0, width, height); XFreeGC(dpy, gc); mask = XGetImage(dpy, pmap, 0, 0, width, height, 1, XYPixmap); XFreePixmap(dpy, pmap); im_bits->b_bits = (BitmapBits *) ZnMalloc(sizeof(BitmapBits)); im_bits->b_bits->pixels = ZnMalloc(height * mask->bytes_per_line); memset(im_bits->b_bits->pixels, 0, height * mask->bytes_per_line); im_bits->b_bits->rowstride = mask->bytes_per_line; im_bits->b_bits->width = width; im_bits->b_bits->height = height; line = im_bits->b_bits->pixels; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { if (XGetPixel(im1, x, y) == XGetPixel(im2, x, y)) { XPutPixel(mask, x, y, 1L); line[x >> 3] |= 0x80 >> (x & 7); } else { full_mask = False; } } line += im_bits->b_bits->rowstride; } XDestroyImage(im2); if (full_mask) { XDestroyImage(mask); im_bits->mask = ZnUnspecifiedPattern; } else { im_bits->mask = mask; } entry = Tcl_CreateHashEntry(&image_bits, image_name, &new); Tcl_SetHashValue(entry, (ClientData) im_bits); } return im_bits; } void InvalidateImage(char *image_name) { Tcl_HashEntry *entry; ImageBits *im_bits; ImagePixmap *im_pmap, *next_im_pmap; if (!image_bits_inited) { return; } /* * Destroy the image entry and wait the cache fault to * reload the new one. */ /*printf("InvalidateImage %s\n", image_name);*/ entry = Tcl_FindHashEntry(&image_bits, image_name); if (entry != NULL) { /*printf("deallocating bits for image %s\n", image_name);*/ im_bits = (ImageBits *) Tcl_GetHashValue(entry); XDestroyImage(im_bits->pixels); if (im_bits->mask) { XDestroyImage(im_bits->mask); } im_pmap = im_bits->pixmaps; while (im_pmap) { next_im_pmap = im_pmap->next; XFreePixmap(im_pmap->dpy, im_pmap->pixmap); if (im_bits->mask) { XFreePixmap(im_pmap->dpy, im_pmap->mask_pmap); } ZnFree(im_pmap); im_pmap = next_im_pmap; } if (im_bits->b_bits) { ZnFree(im_bits->b_bits->pixels); ZnFree(im_bits->b_bits); } #ifdef GLX if (im_bits->texture) { ZnFree(im_bits->t_bits); glDeleteTextures(1, &im_bits->texture); } #endif ZnFree(im_bits); Tcl_DeleteHashEntry(entry); } } /* ********************************************************************************** * * GetImagePixmap -- * ********************************************************************************** */ Pixmap GetImagePixmap(ZnWindow win, char *image_name, ZnImage image, Pixmap *mask_pmap) { ImageBits *im_bits; Display *dpy = Tk_Display(win); int depth = Tk_Depth(win); ImagePixmap *im_pmap, *next_im_pmap; Pixmap pixmap; GC gc; XGCValues values; im_bits = GetImageBits(win, image_name, image); im_pmap = next_im_pmap = im_bits->pixmaps; while (next_im_pmap && (im_pmap->dpy != dpy)) { im_pmap = next_im_pmap; next_im_pmap = next_im_pmap->next; } if (!im_pmap || (im_pmap->dpy != dpy)) { /* printf("New pixmap\n");*/ next_im_pmap = (ImagePixmap *) ZnMalloc(sizeof(ImagePixmap)); next_im_pmap->next = NULL; next_im_pmap->dpy = dpy; pixmap = XCreatePixmap(dpy, ZnWindowId(win), im_bits->width, im_bits->height, depth); gc = XCreateGC(dpy, pixmap, 0, NULL); XPutImage(dpy, pixmap, gc, im_bits->pixels, 0, 0, 0, 0, im_bits->width, im_bits->height); XFreeGC(dpy, gc); next_im_pmap->pixmap = pixmap; if (im_bits->mask) { next_im_pmap->mask_pmap = XCreatePixmap(dpy, ZnWindowId(win), im_bits->width, im_bits->height, 1); values.foreground = 1; values.background = 0; gc = XCreateGC(dpy, next_im_pmap->mask_pmap, GCForeground|GCBackground, &values); XPutImage(dpy, next_im_pmap->mask_pmap, gc, im_bits->mask, 0, 0, 0, 0, im_bits->width, im_bits->height); XFreeGC(dpy, gc); if (mask_pmap) { *mask_pmap = next_im_pmap->mask_pmap; } } else { next_im_pmap->mask_pmap = None; } if (im_pmap) { im_pmap->next = next_im_pmap; } else { im_bits->pixmaps = next_im_pmap; } } else { /*printf("Reusing pixmap\n");*/ pixmap = im_pmap->pixmap; if (mask_pmap) { *mask_pmap = im_pmap->mask_pmap; } } return pixmap; } /* ********************************************************************************** * * GetBitmapMask -- * ********************************************************************************** */ BitmapBits * GetBitmapMask(Display *dpy, Pixmap bitmap) { static int inited = 0; BitmapKey key; Tcl_HashEntry *entry; XImage *mask; BitmapBits *b_bits; unsigned char *line; int x, y, w, h, new; if (!inited) { Tcl_InitHashTable(&bitmap_masks, (sizeof(BitmapKey)) / sizeof(int)); inited = 1; } key.dpy = dpy; key.bitmap = bitmap; entry = Tcl_FindHashEntry(&bitmap_masks, (char *) &key); if (entry != NULL) { b_bits = (BitmapBits *) Tcl_GetHashValue(entry); } else { Tk_SizeOfBitmap(dpy, bitmap, &w, &h); mask = XGetImage(dpy, bitmap, 0, 0, w, h, 1L, XYPixmap); b_bits = (BitmapBits *) ZnMalloc(sizeof(BitmapBits)); b_bits->pixels = ZnMalloc(h * mask->bytes_per_line); memset(b_bits->pixels, 0, h * mask->bytes_per_line); b_bits->rowstride = mask->bytes_per_line; b_bits->width = w; b_bits->height = h; line = b_bits->pixels; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (XGetPixel(mask, x, y)) { line[x >> 3] |= 0x80 >> (x & 7); } } line += b_bits->rowstride; } XDestroyImage(mask); key.dpy = dpy; key.bitmap = bitmap; entry = Tcl_CreateHashEntry(&bitmap_masks, (char *) &key, &new); Tcl_SetHashValue(entry, (ClientData) b_bits); } return b_bits; } #ifdef GLX #define MAX_GLYPHS_PER_GRAB 512 /* this is big enough for 2^9 glyph * character sets */ typedef struct { short width; short height; short xoffset; short yoffset; short advance; unsigned char *bitmap; } PerGlyphInfo, *PerGlyphInfoPtr; typedef struct { int min_char; int max_char; int max_ascent; int max_descent; int num_glyphs; PerGlyphInfo glyph[1]; } FontInfo, *FontInfoPtr; static FontInfoPtr fontinfo; void getMetric(FontInfoPtr font, int c, TexGlyphInfo *tgi) { PerGlyphInfoPtr glyph; unsigned char *bitmapData; tgi->c = c; if ((c < font->min_char) || (c > font->max_char)) { tgi->width = 0; tgi->height = 0; tgi->xoffset = 0; tgi->yoffset = 0; tgi->dummy = 0; tgi->advance = 0; return; } glyph = &font->glyph[c - font->min_char]; bitmapData = glyph->bitmap; if (bitmapData) { tgi->width = glyph->width; tgi->height = glyph->height; tgi->xoffset = glyph->xoffset; tgi->yoffset = glyph->yoffset; } else { tgi->width = 0; tgi->height = 0; tgi->xoffset = 0; tgi->yoffset = 0; } tgi->dummy = 0; /* printf("\'%c\' %d\n", c, glyph->advance);*/ tgi->advance = glyph->advance; } int glyphCompare(const void *a, const void *b) { unsigned char *c1 = (unsigned char *) a; unsigned char *c2 = (unsigned char *) b; TexGlyphInfo tgi1; TexGlyphInfo tgi2; getMetric(fontinfo, *c1, &tgi1); getMetric(fontinfo, *c2, &tgi2); return tgi2.height - tgi1.height; } void placeGlyph(FontInfoPtr font, int c, unsigned char *texarea, int stride, int x, int y) { PerGlyphInfoPtr glyph; unsigned char *bitmapData; int width, height, spanLength; int i, j; /*printf("x: %d, y: %d, c: %d, texarea: 0x%X\n", x, y, c, texarea);*/ if ((c < font->min_char) || (c > font->max_char)) { return; } glyph = &font->glyph[c - font->min_char]; bitmapData = glyph->bitmap; if (bitmapData) { width = glyph->width; spanLength = (width + 7) / 8; height = glyph->height; for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { texarea[stride * (y+i) + x + j] = (bitmapData[i*spanLength + j/8] & (1<<(j&7))) ? 255 : 0; } } } } FontInfoPtr SuckGlyphsFromServer(ZnWindow win, ZnFont font) { Display *dpy = Tk_Display(win); XFontStruct *fontinfo = NULL; Pixmap offscreen = 0; XImage *image = NULL; GC xgc = 0; XGCValues values; int width, height, pixwidth; int i, j; XCharStruct *charinfo; XChar2b character; unsigned char *bitmapData = NULL; int x, y; int numchars, spanLength; int charWidth, charHeight, maxSpanLength; int grabList[MAX_GLYPHS_PER_GRAB]; int glyphsPerGrab = MAX_GLYPHS_PER_GRAB; int numToGrab, thisglyph; FontInfoPtr myfontinfo = NULL; fontinfo = XQueryFont(dpy, Tk_FontId(font)); if (!fontinfo) { return NULL; } numchars = fontinfo->max_char_or_byte2 - fontinfo->min_char_or_byte2 + 1; if (numchars < 1) { return NULL; } myfontinfo = (FontInfoPtr) ZnMalloc(sizeof(FontInfo) + (numchars - 1) * sizeof(PerGlyphInfo)); if (!myfontinfo) { return NULL; } myfontinfo->num_glyphs = numchars; myfontinfo->min_char = fontinfo->min_char_or_byte2; myfontinfo->max_char = fontinfo->max_char_or_byte2; myfontinfo->max_ascent = fontinfo->max_bounds.ascent; myfontinfo->max_descent = fontinfo->max_bounds.descent; width = fontinfo->max_bounds.rbearing - fontinfo->min_bounds.lbearing; height = fontinfo->max_bounds.ascent + fontinfo->max_bounds.descent; maxSpanLength = (width + 7) / 8; /* Be careful determining the width of the pixmap; the X protocol allows pixmaps of width 2^16-1 (unsigned short size) but drawing coordinates max out at 2^15-1 (signed short size). If the width is too large, we need to limit the glyphs per grab. */ if ((glyphsPerGrab * 8 * maxSpanLength) >= (1 << 15)) { glyphsPerGrab = (1 << 15) / (8 * maxSpanLength); } pixwidth = glyphsPerGrab * 8 * maxSpanLength; offscreen = XCreatePixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), pixwidth, height, 1); values.font = Tk_FontId(font); values.background = 0; values.foreground = 0; xgc = XCreateGC(dpy, offscreen, GCFont | GCBackground | GCForeground, &values); XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height); XSetForeground(dpy, xgc, 1); numToGrab = 0; if (fontinfo->per_char == NULL) { charinfo = &(fontinfo->min_bounds); charWidth = charinfo->rbearing - charinfo->lbearing; charHeight = charinfo->ascent + charinfo->descent; spanLength = (charWidth + 7) / 8; } for (i = 0; i < myfontinfo->num_glyphs; i++) { if (fontinfo->per_char != NULL) { charinfo = &(fontinfo->per_char[i]); charWidth = charinfo->rbearing - charinfo->lbearing; charHeight = charinfo->ascent + charinfo->descent; if (charWidth == 0 || charHeight == 0) { /* Still must move raster pos even if empty character */ myfontinfo->glyph[i].width = 0; myfontinfo->glyph[i].height = 0; myfontinfo->glyph[i].xoffset = 0; myfontinfo->glyph[i].yoffset = 0; myfontinfo->glyph[i].advance = charinfo->width; myfontinfo->glyph[i].bitmap = NULL; goto PossiblyDoGrab; } } grabList[numToGrab] = i; /* XXX is this right for large fonts? */ character.byte2 = (i + fontinfo->min_char_or_byte2) & 255; character.byte1 = (i + fontinfo->min_char_or_byte2) >> 8; XDrawString16(dpy, offscreen, xgc, -charinfo->lbearing + 8 * maxSpanLength * numToGrab, charinfo->ascent, &character, 1); numToGrab++; PossiblyDoGrab: if ((numToGrab >= glyphsPerGrab) || (i == myfontinfo->num_glyphs - 1)) { image = XGetImage(dpy, offscreen, 0, 0, pixwidth, height, 1, XYPixmap); for (j = 0; j < numToGrab; j++) { thisglyph = grabList[j]; if (fontinfo->per_char != NULL) { charinfo = &(fontinfo->per_char[thisglyph]); charWidth = charinfo->rbearing - charinfo->lbearing; charHeight = charinfo->ascent + charinfo->descent; spanLength = (charWidth + 7) / 8; } bitmapData = ZnMalloc(height * spanLength * sizeof(char)); memset(bitmapData, 0, height * spanLength * sizeof(char)); if (bitmapData == NULL) { goto FreeFontAndReturn; } for (y = 0; y < charHeight; y++) { for (x = 0; x < charWidth; x++) { /* XXX The algorithm used to suck across the font ensures that each glyph begins on a byte boundary. In theory this would make it convienent to copy the glyph into a byte oriented bitmap. We actually use the XGetPixel function to extract each pixel from the image which is not that efficient. We could either do tighter packing in the pixmap or more efficient extraction from the image. Oh well. */ if (XGetPixel(image, j * maxSpanLength * 8 + x, y)) { bitmapData[y * spanLength + x / 8] |= (1 << (x & 7)); } } } myfontinfo->glyph[thisglyph].width = charWidth; myfontinfo->glyph[thisglyph].height = charHeight; myfontinfo->glyph[thisglyph].xoffset = charinfo->lbearing; myfontinfo->glyph[thisglyph].yoffset = charinfo->descent; myfontinfo->glyph[thisglyph].advance = charinfo->width; myfontinfo->glyph[thisglyph].bitmap = bitmapData; } XDestroyImage(image); numToGrab = 0; /* do we need to clear the offscreen pixmap to get more? */ if (i < myfontinfo->num_glyphs - 1) { XSetForeground(dpy, xgc, 0); XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height); XSetForeground(dpy, xgc, 1); } } } XFreeFontInfo(NULL, fontinfo, 1); XFreeGC(dpy, xgc); XFreePixmap(dpy, offscreen); return myfontinfo; FreeFontAndReturn: XFreeFontInfo(NULL, fontinfo, 1); XDestroyImage(image); XFreeGC(dpy, xgc); XFreePixmap(dpy, offscreen); for (i = 0; i < myfontinfo->num_glyphs; i++) { if (myfontinfo->glyph[i].bitmap) ZnFree(myfontinfo->glyph[i].bitmap); } ZnFree(myfontinfo); return NULL; } /* ********************************************************************************** * * GetTexFont -- * ********************************************************************************** */ TexFont * GetTexFont(ZnWindow win, ZnFont font) { unsigned char *glisto = " ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijmklmnopqrstuvwxyz?.;,!*:\"'èéêëïîôâàçûùü/+@#$%^&()\\-_<>\t\024\025\026\027"; unsigned char *glist=NULL, *glist2=NULL; TexFont *txf; TexGlyphInfo *tgi; int i, j; int min_glyph, max_glyph; int gap = 1; /* gap between glyphs */ int px, py, maxheight; int new, width, height; GLfloat xstep, ystep, texw, texh; static int inited = 0; Tcl_HashEntry *entry; int max_tex_size[1]; if (!inited) { Tcl_InitHashTable(&font_textures, sizeof(ZnFont)/sizeof(int)); inited = 1; } entry = Tcl_FindHashEntry(&font_textures, (char *) font); if (entry != NULL) { return (TexFont *) Tcl_GetHashValue(entry); } else { txf = (TexFont *) ZnMalloc(sizeof(TexFont)); if (txf == NULL) { goto error; } glGetIntegerv(GL_MAX_TEXTURE_SIZE, max_tex_size); txf->tgi = NULL; txf->tgvi = NULL; txf->lut = NULL; txf->teximage = NULL; /* * Initial size of texture. * Assume that max_tex_size is at least 256 texels. */ texw = 128; texh = 64; xstep = 0/*0.5 / texw*/; ystep = 0/*0.5 / texh*/; txf->teximage = ZnMalloc(texw * texh * sizeof(unsigned char)); if (txf->teximage == NULL) { goto error; } /*memset(txf->teximage, 0x55, texw * texh * sizeof(unsigned char));*/ fontinfo = SuckGlyphsFromServer(win, font); if (fontinfo == NULL) { goto error; } txf->max_ascent = fontinfo->max_ascent; txf->max_descent = fontinfo->max_descent; txf->num_glyphs = strlen(glisto); txf->tgi = (TexGlyphInfo *) ZnMalloc(txf->num_glyphs * sizeof(TexGlyphInfo)); if (txf->tgi == NULL) { goto error; } txf->tgvi = (TexGlyphVertexInfo *) ZnMalloc(txf->num_glyphs * sizeof(TexGlyphVertexInfo)); if (txf->tgvi == NULL) { goto error; } glist = ZnMalloc((txf->num_glyphs+1) * sizeof(unsigned char)); strcpy(glist, glisto); qsort(glist, txf->num_glyphs, sizeof(unsigned char), glyphCompare); /* * Keep a cache a the sorted list in case we need to * restart the allocation process. */ glist2 = ZnMalloc((txf->num_glyphs+1) * sizeof(unsigned char)); strcpy(glist2, glist); restart: px = gap; py = gap; maxheight = 0; for (i = 0; i < txf->num_glyphs; i++) { if (glist[i] != 0) { /* If not already processed... */ int foundWidthFit = 0; int c; /* Try to find a character from the glist that will fit on the remaining space on the current row. */ tgi = &txf->tgi[i]; getMetric(fontinfo, glist[i], tgi); width = tgi->width; height = tgi->height; if ((height > 0) && (width > 0)) { for (j = i; j < txf->num_glyphs;) { if ((height > 0) && (width > 0)) { if (px + width + gap < texw) { foundWidthFit = 1; if (j != i) { i--; /* Step back so i loop increment leaves us at same character. */ } break; } } do { j++; } while (glist[j] == 0); if (j < txf->num_glyphs) { tgi = &txf->tgi[j]; getMetric(fontinfo, glist[j], tgi); width = tgi->width; height = tgi->height; } } /* If a fit was found, use that character; otherwise, advance a line in the texture. */ if (foundWidthFit) { if (height > maxheight) { maxheight = height; } c = j; } else { tgi = &txf->tgi[i]; getMetric(fontinfo, glist[i], tgi); width = tgi->width; height = tgi->height; py += maxheight + gap; px = gap; maxheight = height; if (py + height + gap >= texh) { if (texh*2 < max_tex_size[0]) { texh *= 2; ZnFree(txf->teximage); txf->teximage = ZnMalloc(texw * texh * sizeof(unsigned char)); strcpy(glist, glist2); goto restart; } else if (texw*2 < max_tex_size[0]) { texw *= 2; ZnFree(txf->teximage); txf->teximage = ZnMalloc(texw * texh * sizeof(unsigned char)); strcpy(glist, glist2); goto restart; } else { /* Overflowed texture space */ ZnWarning("Font texture overflow"); goto error; } } c = i; } /* Place the glyph in the texture image. */ placeGlyph(fontinfo, glist[c], txf->teximage, texw, px, py); /* Assign glyph's texture coordinate. */ tgi->x = px; tgi->y = py; /* Advance by glyph width, remaining in the current line. */ px += width + gap; } else { /* No texture image; assign invalid bogus texture coordinates. */ tgi->x = -1; tgi->y = -1; c = i; } glist[c] = 0; /* Mark processed; don't process again. */ txf->tgvi[c].t0[0] = tgi->x / texw + xstep; txf->tgvi[c].t0[1] = tgi->y / texh + ystep; txf->tgvi[c].v0[0] = tgi->xoffset; txf->tgvi[c].v0[1] = tgi->yoffset - tgi->height; txf->tgvi[c].t1[0] = (tgi->x + tgi->width) / texw + xstep; txf->tgvi[c].t1[1] = tgi->y / texh + ystep; txf->tgvi[c].v1[0] = (tgi->xoffset + tgi->width); txf->tgvi[c].v1[1] = tgi->yoffset - tgi->height; txf->tgvi[c].t2[0] = (tgi->x + tgi->width) / texw + xstep; txf->tgvi[c].t2[1] = (tgi->y + tgi->height) / texh + ystep; txf->tgvi[c].v2[0] = (tgi->xoffset + tgi->width); txf->tgvi[c].v2[1] = tgi->yoffset; txf->tgvi[c].t3[0] = tgi->x / texw + xstep; txf->tgvi[c].t3[1] = (tgi->y + tgi->height) / texh + ystep; txf->tgvi[c].v3[0] = tgi->xoffset; txf->tgvi[c].v3[1] = tgi->yoffset; txf->tgvi[c].advance = tgi->advance; } } min_glyph = txf->tgi[0].c; max_glyph = txf->tgi[0].c; for (i = 1; i < txf->num_glyphs; i++) { if (txf->tgi[i].c < min_glyph) { min_glyph = txf->tgi[i].c; } if (txf->tgi[i].c > max_glyph) { max_glyph = txf->tgi[i].c; } } txf->tex_width = texw; txf->tex_height = texh; /*printf("texture width: %g, texture height: %g\n", texw, texh);*/ /* printf("min glyph: (%d) \"%c\", max glyph: (%d) \"%c\"\n", min_glyph, min_glyph, max_glyph, max_glyph);*/ txf->min_glyph = min_glyph; txf->range = max_glyph - min_glyph + 1; txf->lut = (TexGlyphVertexInfo **) ZnMalloc(txf->range * sizeof(TexGlyphVertexInfo *)); memset(txf->lut, 0, txf->range * sizeof(TexGlyphVertexInfo *)); if (txf->lut == NULL) { goto error; } for (i = 0; i < txf->num_glyphs; i++) { txf->lut[txf->tgi[i].c - txf->min_glyph] = &txf->tgvi[i]; } glGenTextures(1, &txf->texobj); glBindTexture(GL_TEXTURE_2D, txf->texobj); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY4, txf->tex_width, txf->tex_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, txf->teximage); for (i = 0; i < fontinfo->num_glyphs; i++) { if (fontinfo->glyph[i].bitmap) ZnFree(fontinfo->glyph[i].bitmap); } ZnFree(fontinfo); ZnFree(glist); ZnFree(glist2); entry = Tcl_CreateHashEntry(&font_textures, (char *) font, &new); Tcl_SetHashValue(entry, (ClientData) txf); return txf; } error: if (glist) { ZnFree(glist); } if (glist2) { ZnFree(glist2); } if (fontinfo) { for (i = 0; i < fontinfo->num_glyphs; i++) { if (fontinfo->glyph[i].bitmap) ZnFree(fontinfo->glyph[i].bitmap); } ZnFree(fontinfo); } if (txf) { if (txf->tgi) { ZnFree(txf->tgi); } if (txf->tgvi) { ZnFree(txf->tgvi); } if (txf->lut) { ZnFree(txf->lut); } if (txf->teximage) { ZnFree(txf->teximage); } ZnFree(txf); } return NULL; } /* * Working only for 16 bits displays with 5r6g5b mask, * and 24/32 bits displays. Byte ordering ok on Intel * plateform only. */ void From5r6g5b(unsigned char *data, int width, int height, int bytes_per_line, int t_width, int t_height, BitmapBits *b_bits, unsigned char *t_bits) { int x, y; int rowstride = t_width * 4; unsigned char *obptr; unsigned char *bptr, *bp2; unsigned char alpha; unsigned short temp; bptr = t_bits; for (y = 0; y < height; y++) { bp2 = bptr; obptr = data; for (x = 0; x < width; x++) { /* * Configure the alpha value. */ alpha = GetBitmapPixel(b_bits, x, y) ? 255 : 0; /* * Dispatch the 3 color components. */ temp = ((unsigned short *)obptr)[0]; *bp2 = (temp >> 8) & 0xf8; /* r */ bp2++; *bp2 = (temp >> 3) & 0xfc; /* v */ bp2++; *bp2 = (temp << 3); /* b */ bp2++; *bp2 = alpha; bp2++; obptr += 2; } for (x = width; x < t_width; x++) { *bp2 = 0; bp2++; *bp2 = 0; bp2++; *bp2 = 0; bp2++; *bp2 = 0; bp2++; } bptr += rowstride; data += bytes_per_line; } for (y = height; y < t_height; y++) { memset(bptr, 0, rowstride); bptr += rowstride; } } void From8r8g8b(unsigned char *data, int width, int height, int bytes_per_line, int t_width, int t_height, BitmapBits *b_bits, unsigned char *t_bits) { int x, y; int rowstride = t_width * 4; unsigned char *obptr; unsigned char *bptr, *bp2; unsigned char alpha; bptr = t_bits; for (y = 0; y < height; y++) { bp2 = bptr; obptr = data; for (x = 0; x < width; x++) { /* * Configure the alpha value. */ alpha = GetBitmapPixel(b_bits, x, y) ? 255 : 0; /* * Dispatch the 3 color components. * Be careful the Red and Blue are swapped it works on an Intel * plateform but may need some more tests to be fully generic. */ *bp2++ = obptr[2]; /* r */ *bp2++ = obptr[1]; /* v */ *bp2++ = obptr[0]; /* b */ obptr += 4; *bp2++ = alpha; } for (x = width; x < t_width; x++) { *bp2 = 0; bp2++; *bp2 = 0; bp2++; *bp2 = 0; bp2++; *bp2 = 0; bp2++; } bptr += rowstride; data += bytes_per_line; } for (y = height; y < t_height; y++) { memset(bptr, 0, rowstride); bptr += rowstride; } } static int To2Power(int a) { int result = 1; while (result < a) { result *= 2; } return result; } ImageBits * GetImageTexture(ZnWindow win, char *image_name, ZnImage image) { ImageBits *im_bits; int t_width, t_height; int depth = Tk_Depth(win); /* int error;*/ im_bits = GetImageBits(win, image_name, image); if (!im_bits->t_bits) { t_width = To2Power(im_bits->width); t_height = To2Power(im_bits->height); im_bits->s = im_bits->width / (ZnReal) t_width; im_bits->t = im_bits->height / (ZnReal) t_height; im_bits->t_bits = ZnMalloc(t_width * 4 * t_height); if (depth == 16) { From5r6g5b(im_bits->pixels->data, im_bits->width, im_bits->height, im_bits->pixels->bytes_per_line, t_width, t_height, im_bits->b_bits, im_bits->t_bits); } else if (depth == 24) { From8r8g8b(im_bits->pixels->data, im_bits->width, im_bits->height, im_bits->pixels->bytes_per_line, t_width, t_height, im_bits->b_bits, im_bits->t_bits); } glGenTextures(1, &im_bits->texture); /*printf("creation texture %d pour image %s\n", im_bits->texture, image_name);*/ glBindTexture(GL_TEXTURE_2D, im_bits->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, t_width, t_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, im_bits->t_bits); /* error = glGetError(); if (error != GL_NO_ERROR) { printf("GetImageTexture: %s\n", gluErrorString(error)); } */ } return im_bits; } BitmapBits * GetBitmapTexture(Display *dpy, Pixmap bitmap) { BitmapBits *b_bits; int t_width, t_height; int i, j; unsigned char *o, *ostart, *d, *dstart; b_bits = GetBitmapMask(dpy, bitmap); if (!b_bits->texture) { t_width = To2Power(b_bits->width); t_height = To2Power(b_bits->height); b_bits->s = b_bits->width / (ZnReal) t_width; b_bits->t = b_bits->height / (ZnReal) t_height; b_bits->t_bits = ZnMalloc(t_width * t_height); memset(b_bits->t_bits, 0, t_width * t_height); ostart = b_bits->pixels; dstart = b_bits->t_bits; for (i = 0; i < b_bits->height; i++) { d = dstart; o = ostart; for (j = 0; j < b_bits->width; j++) { *d++ = GetBitmapPixel(b_bits, j, i) ? 255 : 0; } ostart += b_bits->rowstride; dstart += t_width; } glGenTextures(1, &b_bits->texture); glBindTexture(GL_TEXTURE_2D, b_bits->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY4, t_width, t_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, b_bits->t_bits); } return b_bits; } #endif