/* * Copyright (C) 1997-2005, R3vis Corporation. * * This library 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 library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA, or visit http://www.gnu.org/copyleft/lgpl.html. * * Original Contributor: * Wes Bethel, R3vis Corporation, Marin County, California * Additional Contributor(s): * * The OpenRM project is located at http://openrm.sourceforge.net/. */ /* * $Id: rmutil.c,v 1.11 2005/06/06 02:04:29 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.11 $ * $Log: rmutil.c,v $ * Revision 1.11 2005/06/06 02:04:29 wes * Lots of small additions to clean up compiler warnings. * * Revision 1.10 2005/02/27 19:34:04 wes * Added support for application supplied texture object IDs and display lists. * * Revision 1.9 2005/02/19 16:40:20 wes * Distro sync and consolidation. * Repairs to fix memory leak associated with repeated calls to rmPipeNew, * rmPipeMakeCurrent, rmPipeClose. * * Revision 1.8 2005/01/23 17:00:22 wes * Copyright updated to 2005. * * Revision 1.7 2004/01/16 16:49:13 wes * Updated copyright line for 2004. * * Revision 1.6 2003/11/05 15:33:10 wes * Removed rmStartTimer and rmElapsedTime calls; these have been replaced * with a new family of rmTime() routines introduced in 1.5.1. * * Revision 1.5 2003/04/12 19:48:53 wes * Bug fix in RM_SOFTWARE 3D image resize operations. * * Revision 1.4 2003/04/08 02:45:33 wes * Implemented RM_SOFTWARE 3D image resizing (finally). * * Revision 1.3 2003/02/14 00:19:57 wes * Memset the context cache to zero upon initialization. Avoids a bug caused * by having garbage in uninitialized memory. * * Revision 1.2 2003/02/02 02:07:16 wes * Updated copyright to 2003. * * Revision 1.1.1.1 2003/01/28 02:15:23 wes * Manual rebuild of rm150 repository. * * Revision 1.11 2003/01/16 22:21:17 wes * Updated all source files to reflect new organization of header files: * all header files formerly located in include/rmaux, include/rmi, include/rmv * are now located in include/rm. * * Revision 1.10 2002/09/05 15:07:39 wes * Changed "is a texture" test slightly. * * Revision 1.9 2002/08/29 22:20:32 wes * * Massive upgrade to accommodate dynamic object reallocation within * the component manager, and within the context cache. Use the * debug #define DEBUG_LEVEL DEBUG_REALLOC_TRACE to get a printf * whenever a realloc occurs. With this upgrade, there are no * OpenRM limits on the size of the scene graph. There will be external * limits, such as the amount of RAM and the amount of space available * to your OpenGL implementation. * * Revision 1.8 2002/08/17 15:14:36 wes * Changed reference of RM_COMPONENT_POOL_SIZE from a #define to a * global variable. * * Revision 1.7 2002/04/30 19:36:22 wes * Updated copyright dates. * * Revision 1.6 2001/10/15 03:08:40 wes * fix a compile warning. * * Revision 1.5 2001/07/15 17:20:23 wes * Added private routines that manage retained mode resources by * cleaning the context cache. These routines are draconian in approach, * and should be made more selective in future releases. * * Revision 1.4 2001/03/31 17:12:39 wes * v1.4.0-alpha-2 checkin. * * Revision 1.3 2000/12/03 22:35:38 wes * Mods for thread safety. * * Revision 1.2 2000/04/20 16:29:47 wes * Documentation additions/enhancements, some code rearragement. * * Revision 1.1.1.1 2000/02/28 21:29:40 wes * OpenRM 1.2 Checkin * * Revision 1.1.1.1 2000/02/28 17:18:48 wes * Initial entry - pre-RM120 release, source base for OpenRM 1.2. * */ #include #include "rmprivat.h" #ifdef RM_X #include #include #endif /* * static mutex to protect cache key from simultaneous access by * multiple app threads. only one such mutex is needed, so keeping * it around as a static variable is ok as far as thread-safety goes. */ static RMmutex *cacheKeyMutex=NULL; /* * ---------------------------------------------------- * @Name rmNearestPowerOfTwo @pstart int rmNearestPowerOfTwo (int n) @pend @astart int n - an integer value. @aend @dstart This routine computes the integer that is the closest power of two to the input integer "n". The algorithm works only for non-negative input. This routine will come in handy if you have to scale an image so it's size is an even power of two. @dend * ---------------------------------------------------- */ int rmNearestPowerOfTwo (int n) { int nbits, j; j = n; for (nbits = 0; j > 0;) { j = j >> 1; nbits++; } if (nbits != 0) nbits--; if ((1<> 1); c = c>>1; if (b > c) return(1 << nbits); else return(1 << (nbits + 1)); } } /* * ---------------------------------------------------- * @Name rmIntMagnitude @pstart int rmIntMagnitude (int m) @pend @astart int m - an integer value. @aend @dstart This routine computes log2(m) for some integer m, and returns an integer. It is not an exact log2() replacement. It is useful for determining the position of the uppermost "on" bit in an integer. @dend * ---------------------------------------------------- */ int rmIntMagnitude (int m) { int i = 0; while (m > 0) { m = m >> 1; i++; } if (i == 0) return(0); else return(i - 1); } /* * ---------------------------------------------------- * @Name rmHSVtoRGB @pstart void rmHSVtoRGB (float hue, float saturation, float value, float *redReturn, float *greenReturn, float *blueReturn) @pend @astart float hue, saturation, value - floating point values in the range [0..1] (input). float *redReturn, *greenReturn, *blueReturn - handles to floats. will contain floats in the range [0..1] (return). @aend @dstart Convert a three-component pixel from HSV space to RGB space. Input hue is in the range 0..1, with 0 corresponding to 0 degrees on the HSV circle, and 1.0 corresponding to 360 degrees on the HSV circle. Saturation is in the range 0..1, where 0 is fully desaturated and 1 is fully saturated. Value, or brightness, is in the range 0..1. A brightness value of 0 is black (not very bright), and a value of 1.0 is full brightness. The results of the conversion are placed into caller-supplied memory. The return RGB values are also in the range 0..1, with 0 representing "full off" and 1 representing "full on." @dend * ---------------------------------------------------- */ void rmHSVtoRGB (float h, float s, float v, float *r, float *g, float *b) { int i; float f, p, q, t; float tr = 0.0, tg = 0.0, tb = 0.0; /* inits satisfies gcc warning */ float ht; /* (h,s,v) in [0..1] --> (r,g,b) will be in [0..1] - Foley & VanDam */ ht = h; if (v == 0.0) { tr=0.0; tg=0.0; tb=0.0; } else { if (s == 0.0) { tr = v; tg = v; tb = v; } else { ht = ht * 6.0; if (ht >= 6.0) ht = 0.0; i = ht; f = ht - i; p = v * (1.0 - s); q = v * (1.0 - s*f); t = v * (1.0 - (s * (1.0 - f))); if (i == 0) { tr = v; tg = t; tb = p; } else if (i == 1) { tr = q; tg = v; tb = p; } else if (i == 2) { tr = p; tg = v; tb = t; } else if (i == 3) { tr = p; tg = q; tb = v; } else if (i == 4) { tr = t; tg = p; tb = v; } else if (i == 5) { tr = v; tg = p; tb = q; } } } *r = tr; *g = tg; *b = tb; } /* * ---------------------------------------------------- * @Name rmRGBtoHSV @pstart void rmRGBtoHSV (float red, float green, float blue, float *hueReturn, float *saturationReturn, float *valueReturn) @pend @astart float red, green, blue - floating point values in the range 0..1 that represent a 3-component RGB pixel. float *hueReturn, *saturationReturn, *valueReturn - handles to floats that will contain the HSV representation of the input RGB pixel upon return. The return values are in the range 0..1 (result). @aend @dstart Converts an RGB 3-tuple into HSV space. Output hue is in the range 0..1, with 0 corresponding to 0 degrees on the HSV circle, and 1.0 corresponding to 360 degrees on the HSV circle. Saturation is in the range 0..1, where 0 is fully desaturated and 1 is fully saturated. Value, or brightness, is in the range 0..1. A brightness value of 0 is black (not very bright), and a value of 1.0 is full brightness. @dend * ---------------------------------------------------- */ void rmRGBtoHSV (float rr, float gg, float bb, float *hh, /* return: 0 <= h <= 1 */ float *ss, /* return: 0 <= s <= 1 */ float *vv) /* return: 0 <= v <= 1 */ { double min, max, v, s, h, rc, gc, bc, r, g, b; /* (h,s,v) in [0..1] --> (r,g,b) will be in [0..1] - Foley & VanDam */ r = rr; g = gg; b = bb; max = RM_MAX(r, g); max = RM_MAX(max, b); min = RM_MIN(r, g); min = RM_MIN(min, b); v = max; if (max != 0.0) s = (max - min) / max; else s = 0.0; if (s == 0) /* h = UNDEFINED_HUE pick something */ h = 0.0; else { rc = (max - r) / (max - min); gc = (max - g) / (max - min); bc = (max - b) / (max - min); if (r == max) h = bc - gc; else if (g == max) h = 2 + rc - bc; else if (b == max) h = 4 + gc - rc; h = h * 60; if (h < 0.0) h = h + 360.0; } *hh = h / 360.0; *ss = s; *vv = v; } /* * ---------------------------------------------------- * @Name rmUnionBoundingBoxes @pstart RMenum rmUnionBoundingBoxes (const RMvertex3D *s1min, const RMvertex3D *s1max, const RMvertex3D *s2min, const RMvertex3D *s2max, RMvertex3D *dmin, RMvertex3D *dmax) @pend @astart const RMvertex3D *s1min, *s1max, *s2min, *s2max - handles to RMvertex3D objects (input). RMvertex3D *dmin, *dmax - handles to RMvertex3D objects (modified). @aend @dstart This routine performs a "union" operation of two, 3D boxes, returning the minimum and maximum coordinates of the resulting box into caller-supplied memory. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmUnionBoundingBoxes (const RMvertex3D *s1min, const RMvertex3D *s1max, const RMvertex3D *s2min, const RMvertex3D *s2max, RMvertex3D *dmin, RMvertex3D *dmax) { if ((s1min == NULL) || (s1max == NULL) || (s2min == NULL) || (s2max == NULL) || (dmin == NULL) || (dmax == NULL)) { rmError("rmUnionBoundingBoxes() error: one of the input parameters is NULL."); return(RM_WHACKED); } dmin->x = RM_MIN(s1min->x, s2min->x); dmin->y = RM_MIN(s1min->y, s2min->y); dmin->z = RM_MIN(s1min->z, s2min->z); dmax->x = RM_MAX(s1max->x, s2max->x); dmax->y = RM_MAX(s1max->y, s2max->y); dmax->z = RM_MAX(s1max->z, s2max->z); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmGLGetError @pstart int rmGLGetError (const char *string) @pend @astart const char *string - a handle to a string (input). @aend @dstart This routine is a wrapper for the routine glGetError(), and is used to obtain OpenGL error codes. If there are no errors, a value of zero is returned by rmGLGetError(). Otherwise, the error code is returned to the caller, and the error code, along with the message contained in the parameter "string" is reported via rmWarning(). Refer to gl.h in your OpenGL distribution for definitions of the OpenGL error codes, as well as the man page for glGetError(). This routine was used extensively for debugging during the development of RM Scene Graph. @dend * ---------------------------------------------------- */ int rmGLGetError (const char *s) { GLenum rstat = glGetError(); while (rstat != GL_NO_ERROR) { if (rstat != 0) { char buf[128]; sprintf(buf, "%s OpenGL error code (hex): 0x%04x ", s, rstat); rmWarning(buf); } rstat = glGetError(); } return(rstat); } /* * ---------------------------------------------------- * @Name rmImageBuildMipmaps @pstart int rmImageBuildMipmaps (const RMimage *src, RMimage ***mapsReturn, RMenum hardwareEnum, RMpipe *hwPipe) @pend @astart const RMimage *src - a handle to an RMimage object (input). RMimage ***mapsReturn - a pointer to an array of RMimage pointer (return). RMenum hardwareEnum - an RMenum value, may be either RM_HARDWARE or RM_SOFTWARE (input). RMpipe *hwPipe - a handle to an RMpipe (input). A valid RMpipe handle is required when RM_HARDWARE is requested via hardwareEnum. When RM_SOFTWARE image resizing for mipmap generation is requested, callers should specify NULL for the hwPipe parameter. @aend @dstart This convenience routine will generate a full set of mipmaps given an input image. The number of mipmaps generated is returned to the caller. This value will be positive and non-zero upon success, otherwise zero is returned. When RM_SOFTWARE is selected, RM will repeatedly call gluScaleImage to produce the mipmaps, a software operation. When RM_HARDWARE is set, the framebuffer itself is used to scale the image data. In some cases, RM_HARDWARE will run faster by an order of magnitude than RM_SOFTWARE. Inside this routine, an array of RMimage pointer is allocated. The number of entries in this array is log2(max(width,height)) of the input image, and is the number of mipmaps that will be generated. Then, each of these N images is created using RM_COPY_DATA, including the first image (which is a duplicate of the input image). The second image will be 1/4 the size of the first, and so forth down to a 1x1 image. At this time (January 2000), RM_HARDWARE occasionally has problems with the pixel reads on some framebuffers (esp. nVidia cards under Linux). At this time (January 2000), rmImageBuildMipmaps works ONLY on RMimage objects containing a two-dimensional image. There is no support at present for generating 3D mipmaps. At this time (January 2000), the scaled images are drawn to the framebuffer for debugging purposes, regardless of whether or not RM_SOFTWARE or RM_HARDWARE is specified by the caller. It is assumed that the input image is an even power of two in size. The following code snippet shows how to free the images returned by rmImageBuildMipmaps.
 RMimage  *source;
 RMimage **dstMipmaps;
 int       nMipmaps, i;

 ... stuff is assigned to source here (omitted) ...

 nMipmaps = rmImageBuildMipmaps(source, &dstMipmaps, RM_HARDWARE);

 .... do some stuff with the mipmaps (omitted) ...

 // delete the images
 for (i = 0; i < nMipmaps; i++)
     rmImageDelete(dstMipmaps[i]);
 free((void *)dstMipmaps);
 
@dend * ---------------------------------------------------- */ int rmImageBuildMipmaps (const RMimage *src, RMimage ***maps_return, RMenum hardware_enum, RMpipe *hwPipe) { /* hack - right now, we can only compute mipmaps for 2D images */ int nmm; if ((RM_ASSERT(src, "rmImageBuildMipMaps() error: the input RMimage object is NULL") == RM_WHACKED) || (RM_ASSERT(src, "rmImageBuildMipMaps() error: the input maps_return pointer is NULL") == RM_WHACKED)) return(0); if ((hardware_enum == RM_HARDWARE) && (hwPipe == NULL)) { rmError("rmImageBuildMipmaps() error: a valid RMpipe must be specified when using RM_HARDWARE as the mipmap generation method. "); return(0); } if (private_rmImageGetNDims(src) == 2) nmm = private_rmImage2DBuildMipmaps(src, maps_return, hardware_enum, hwPipe); else { rmWarning(" mipmap generation for 3D images is currently under development."); nmm = 0; } return(nmm); /* number of mipmaps generated */ } /* PRIVATE * * this internal routine is used to set up the view and projection * matrices, and a simple viewing mode for the purpose of drawing * image data into the framebuffer. * * October 2000 - rewrote to be thread safe. */ void private_rmInitInternalImagingPipeline (RMpipe *p) { int w, h; /* * it may not be necessary to make the RMpipe "current", as the * application should have made the pipe current already - however, * we're taking a conservative approach. at worst, we incur the * cost of a call to glxMakeCurrent or wglMakeCurrent. */ rmPipeMakeCurrent(p); /* * set matrix parameters for drawing images. */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glPixelZoom((GLfloat)1.0, (GLfloat)1.0); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); rmPipeGetWindowSize(p, &w, &h); /* the glOrtho call needs some tweaking. */ glOrtho(0.0, (double)(w - 1), 0.0, (double)(h - 1), -1.0, 1.0); glViewport(0, 0, w, h); glReadBuffer(GL_FRONT); } /* PRIVATE */ /* * 10/2000 - rewrote to be thread safe. */ int private_rmImage2DBuildMipmaps (const RMimage *img, RMimage ***maps_return, int filter_method, RMpipe *p) { /* * TODO: * * we need to make some checks if we're using hardware for scaling: * 1. is the currently defined window large enough to handle our * imaging requirements? * 2. if not, what do we do about it? * a. fall back onto software * b. resize the window to fit the problem? it may still be possible * to exceed resources on the display with large images which * won't fit into the window. */ unsigned char *sdata; int w, h, sw, sh; int wmag, hmag; int i, count; int winw, winh; RMimage **tt, *t; RMenum rmformat, rmtype; GLenum format, ptype; rmPipeGetWindowSize(p, &winw, &winh); w = private_rmImageGetWidth(img); h = private_rmImageGetHeight(img); sw = rmNearestPowerOfTwo(w); sh = rmNearestPowerOfTwo(h); /* need to check if sw,sh exceed the size of the current window. for now, we'll just report an error. later we may want to take more positive action, such as temporarily resizing the window for use in imaging operations. */ { /* fprintf(stderr,"rmImage2DBuildMipaps: need to update code to use pipe for window dims. \n"); */ if ((sw > winw) || (sh > winh)) { char buf[512]; sprintf(buf, "%s %d by %d", " The current window is not large enough to accomodate texture resizing in hardware. Either increase the size of the window or decrease the size of the texture (NOTE: later versions of RM will temporarily resize the window). Parts of the resized texture may appear 'blacked out.' We need a window of at least \n", sw, sh); rmWarning(buf); } } wmag = rmIntMagnitude(sw); hmag = rmIntMagnitude(sh); /* figure out how many images to create */ count = RM_MAX(wmag, hmag) + 1; rmformat = private_rmImageGetFormat(img); rmtype = private_rmImageGetType(img); tt = *maps_return = (RMimage **)malloc(sizeof(RMimage *)*count); private_rmInitInternalImagingPipeline(p); sdata = private_rmImageGetPixelData(img); format = private_rmImageGetOGLFormat(img); ptype = private_rmImageGetOGLType(img); /* * note: format as returned by that routine isn't exactly correct * when the image type is one of the indexed types. for example, if * we have raw scalars, as is the case when we have indexed images, * that routine returns GL_RGB for RM_IMAGE_INDEXED_RGB. do we want * blat out raw scalars as, say, luminance or try to do something else? */ glDrawBuffer(GL_FRONT); /* ?? we could put this into the back buffer and the user would never see it..*/ /* this draws the original image. not needed, but cute */ glRasterPos2f(0.0,0.0); glDrawPixels(w, h, format, ptype, sdata); glFinish(); for (i = 0; i < count; i++) { unsigned char *dest_data; int tw, th; int status; float xzoom, yzoom; tw = RM_MAX((sw >> i), 1); th = RM_MAX((sh >> i), 1); tt[i] = t = rmImageNew(2, tw, th, 1, rmformat, rmtype, RM_COPY_DATA); if (i == 0) { xzoom = (float)tw / (float)w; yzoom = (float)th / (float)h; } else { xzoom = 0.5; yzoom = 0.5; } { unsigned char *image_data; int lastw, lasth; if (i == 0) { /* draw the original image, but with zoom */ image_data = sdata; lastw = w; lasth = h; } else { rmImageGetImageSize(tt[i - 1], NULL, &lastw, &lasth, NULL, NULL, NULL); image_data = private_rmImageGetPixelData(tt[i - 1]); } dest_data = private_rmImageGetPixelData(t); if (filter_method == RM_HARDWARE) { glPixelZoom((GLfloat)xzoom, (GLfloat)yzoom); glRasterPos2f(0.0, 0.0); glDrawPixels(lastw, lasth, format, ptype, (const void *)image_data); glFinish(); glReadBuffer(GL_FRONT); if (format == GL_LUMINANCE) { glPixelTransferf(GL_RED_SCALE, (GLfloat)0.3); glPixelTransferf(GL_RED_BIAS, (GLfloat)0.0); glPixelTransferf(GL_GREEN_SCALE, (GLfloat)0.59); glPixelTransferf(GL_GREEN_BIAS, (GLfloat)0.0); glPixelTransferf(GL_BLUE_SCALE, (GLfloat)0.1); glPixelTransferf(GL_BLUE_BIAS, (GLfloat)0.0); } /* read the zoomed image back into a local buffer */ glReadPixels(0, 0, tw, th, format, ptype, dest_data); if (format == GL_LUMINANCE) { glPixelTransferf(GL_RED_SCALE, (GLfloat)1.0); glPixelTransferf(GL_GREEN_SCALE, (GLfloat)1.0); glPixelTransferf(GL_BLUE_SCALE, (GLfloat)1.0); } glFinish(); } else { /* this uses the glu software image scaler rather than the hardware imaging pipe */ status = gluScaleImage(format, w, h, ptype, (const void *)(sdata), tw, th, ptype, (void *)(dest_data)); } } glPixelZoom((GLfloat)1.0, (GLfloat)1.0); glClear(GL_COLOR_BUFFER_BIT); glRasterPos2f(0.0, 0.0); glDrawPixels(tw, th, format, ptype, dest_data); glFinish(); } return(count); } /* PRIVATE * * here is the guts of the 3D mipmap generator. it's not wired * in at this time, but has been used experimentally in the past. * * jan 2000 - i'm not sure what the status is of this routine at * this time...it probably needs to be rewritten. */ RMenum private_rmImage3DResize (const RMimage *src, RMimage *dst, RMenum hardware_enum, RMpipe *hwPipe) { /* * 1. for each w x h slice of the source volume, use hardware to * rescale the image to new size. * 2. read the pixels back from the framebuffer and dump them to * a temp area (file? memory?) * 3. for each w x d slice of the intermediate volume, resize that * image to the new size. * 4. read the pixels back from the framebuffer and stuff them * into "dst" */ /* * this routine used to have "scale" and "bias" as input parms. why? * todo: write software version. */ unsigned char *src_data, *sdata; unsigned char *dest_data, *ddata; unsigned char *intermed_space; /* hold intermediate step in memory..hope this machine can swap or has a bunch of memory */ unsigned char *idata; unsigned char *wbuf; /* images of size dest_width * source_depth and is used in pass2 processing */ unsigned char *lbuf; /* images of size dst_width * dst_depth used to receive pass2 scaled data. */ int i, stride; int sw, sh, sd; int dw, dh, dd; float scale = 1.0; float bias = 0.0; float xzoom, yzoom; GLenum glformat, ptype; if ((RM_ASSERT(src, "private_rmImage3DResize() error: source RMimage pointer is NULL") == RM_WHACKED) || (RM_ASSERT(dst, "private_rmImage3DResize() error: dest RMimage pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); #if 0 /* this should now be working */ if (hardware_enum == RM_SOFTWARE) rmWarning("private_rmImage3DResize() warning: RM_SOFTWARE is not yet supported. using RM_HARDWARE for image resize."); #endif rmImageGetImageSize(src, NULL, &sw, &sh, &sd, NULL, NULL); rmImageGetImageSize(dst, NULL, &dw, &dh, &dd, NULL, NULL); /* check to see if currently-defined window, etc. can accomodate the size of images that we're going to throw at it. */ if (hardware_enum == RM_HARDWARE) { private_rmInitInternalImagingPipeline(hwPipe); glDrawBuffer(GL_FRONT); /* ?? we could put this into the back */ } /* loop over source depth */ src_data = private_rmImageGetPixelData(src); stride = private_rmImageGetElements(src); glformat = private_rmImageGetOGLFormat(src); ptype = private_rmImageGetOGLType(src); #if 0 srcglformat = private_rmImageGetOGLFormat(src); dstglformat = private_rmImageGetOGLFormat(dst); #endif intermed_space = (unsigned char *)malloc(sizeof(unsigned char) * dw * dh * sd * stride); wbuf = (unsigned char *)malloc(sizeof(unsigned char) * dw * sd * stride); lbuf = (unsigned char *)malloc(sizeof(unsigned char) * dw * dd * stride); if ((intermed_space == NULL) || (wbuf == NULL) || (lbuf == NULL)) { rmError(" private_rmImage3DResize..can't get memory for processing. aborting. \n"); return(RM_WHACKED); } if (stride == -1) { rmError(" unknown image format in rmImage3DResize... aborting \n"); return(RM_WHACKED); } xzoom = (float)dw / (float)sw; yzoom = (float)dh / (float)sh; for (i = 0; i < sd; i++) { sdata = src_data + (i * stride * sw * sh); idata = intermed_space + (i * stride * dw * dh); if (hardware_enum == RM_SOFTWARE) { fake_gluScaleImage(glformat, sw, sh, ptype, sdata, dw, dh, ptype, idata); } else { glRasterPos2f(0.0, 0.0); glPixelZoom((GLfloat)xzoom, (GLfloat)yzoom); glDrawPixels(sw, sh, glformat, ptype, sdata); glReadBuffer(GL_FRONT); if (glformat == GL_LUMINANCE) { glPixelTransferf(GL_RED_SCALE, (GLfloat)0.3 * scale); glPixelTransferf(GL_RED_BIAS, (GLfloat)bias); glPixelTransferf(GL_GREEN_SCALE, (GLfloat)0.59 * scale); glPixelTransferf(GL_GREEN_BIAS, (GLfloat)bias); glPixelTransferf(GL_BLUE_SCALE, (GLfloat)0.1 * scale); glPixelTransferf(GL_BLUE_BIAS, (GLfloat)bias); } else { glPixelTransferf(GL_RED_SCALE, (GLfloat)scale); glPixelTransferf(GL_RED_BIAS, (GLfloat)bias); glPixelTransferf(GL_GREEN_SCALE, (GLfloat)scale); glPixelTransferf(GL_GREEN_BIAS, (GLfloat)bias); glPixelTransferf(GL_BLUE_SCALE, (GLfloat)scale); glPixelTransferf(GL_BLUE_BIAS, (GLfloat)bias); } /* read the zoomed image back into a local buffer */ glPixelZoom((GLfloat)1.0f, (GLfloat)1.0F); glReadPixels(0, 0, dw, dh, glformat, ptype, idata); if (glformat == GL_LUMINANCE) { glPixelTransferf(GL_RED_SCALE, (GLfloat)1.0); glPixelTransferf(GL_GREEN_SCALE, (GLfloat)1.0); glPixelTransferf(GL_BLUE_SCALE, (GLfloat)1.0); glPixelTransferf(GL_RED_BIAS, (GLfloat)0.0); glPixelTransferf(GL_GREEN_BIAS, (GLfloat)0.0); glPixelTransferf(GL_BLUE_BIAS, (GLfloat)0.0); } } /* end RM_HARDWARE */ } dest_data = private_rmImageGetPixelData(dst); src_data = intermed_space; /* done with pass 1. now do pass 2. */ xzoom = 1.0; yzoom = (float)dd / (float)sd; if (hardware_enum == RM_HARDWARE) glPixelZoom((GLfloat)xzoom, (GLfloat)yzoom); for (i = 0; i < dh; i++) { int id, tstride, wbufstride; unsigned char *wbuf2; /* load up temp image from holding buffer */ sdata = src_data + (i * dw * stride); tstride = dw * dh * stride; wbuf2 = wbuf; wbufstride = dw * stride; for (id = 0; id < sd; id++) { memcpy((wbuf2), (sdata), (sizeof(unsigned char) * dw * stride)); sdata += tstride; wbuf2 += wbufstride; } /* now that we have a 2d image (representing a w x d slice of the intermediate volume) blast it down the imaging pipeline */ if (hardware_enum == RM_SOFTWARE) { fake_gluScaleImage(glformat, dw, sd, ptype, wbuf, dw, dd, ptype, lbuf); } else { glPixelZoom((GLfloat)xzoom, (GLfloat)yzoom); glRasterPos2f(0.0, 0.0); glDrawPixels(dw, sd, glformat, ptype, wbuf); glReadBuffer(GL_FRONT); if (glformat == GL_LUMINANCE) { glPixelTransferf(GL_RED_SCALE, (GLfloat)0.3 * scale); glPixelTransferf(GL_RED_BIAS, (GLfloat)bias); glPixelTransferf(GL_GREEN_SCALE, (GLfloat)0.59 * scale); glPixelTransferf(GL_GREEN_BIAS, (GLfloat)bias); glPixelTransferf(GL_BLUE_SCALE, (GLfloat)0.1 * scale); glPixelTransferf(GL_BLUE_BIAS, (GLfloat)bias); } else { glPixelTransferf(GL_RED_SCALE, (GLfloat)scale); glPixelTransferf(GL_RED_BIAS, (GLfloat)bias); glPixelTransferf(GL_GREEN_SCALE, (GLfloat)scale); glPixelTransferf(GL_GREEN_BIAS, (GLfloat)bias); glPixelTransferf(GL_BLUE_SCALE, (GLfloat)scale); glPixelTransferf(GL_BLUE_BIAS, (GLfloat)bias); } /* read the zoomed image back into a local buffer. */ glPixelZoom((GLfloat)1.0f, (GLfloat)1.0f); glReadPixels(0, 0, dw, dd, glformat, ptype, lbuf); if (glformat == GL_LUMINANCE) { glPixelTransferf(GL_RED_SCALE, (GLfloat)1.0); glPixelTransferf(GL_GREEN_SCALE, (GLfloat)1.0); glPixelTransferf(GL_BLUE_SCALE, (GLfloat)1.0); glPixelTransferf(GL_RED_BIAS, (GLfloat)0.0); glPixelTransferf(GL_GREEN_BIAS, (GLfloat)0.0); glPixelTransferf(GL_BLUE_BIAS, (GLfloat)0.0); } } /* end RM_HARDWARE */ /* now, stuff from lbuf into the final volume buffer */ ddata = dest_data + (i * dw * stride); tstride = dw * dh * stride; wbufstride = dw * stride; wbuf2 = lbuf; for (id = 0; id < dd; id++) { memcpy((void *)ddata, (void *)wbuf2, (sizeof(unsigned char) * dw * stride)); ddata += tstride; wbuf2 += wbufstride; } } free((void *)lbuf); free((void *)wbuf); free((void *)intermed_space); return(RM_CHILL); } /* PRIVATE */ RMenum private_rmInitCacheKeyMutex(void) { cacheKeyMutex = rmMutexNew(RM_MUTEX_UNLOCK); if (cacheKeyMutex == NULL) { rmError("private_rmInitCacheKeyMutex() error: problem initializing cache key mutex. cache keys are not guaranteed to be unique in the presence of multiple threads."); return(RM_WHACKED); } return(RM_CHILL); } /* PRIVATE */ RMenum private_rmDestroyCacheKeyMutex(void) { rmMutexDelete(cacheKeyMutex); return RM_CHILL; } /* PRIVATE */ RMcacheKey private_rmGetNewCacheKey(void) { RMcacheKey rval; static unsigned int counter=0; /* * there's only one component manager, regardless of the * number of app threads. we get a cache key that is guaranteed * to be unique simply by protecting a counter with a mutex * to ensure caller access is serialized. */ if (cacheKeyMutex != NULL) rmMutexLock(cacheKeyMutex); rval = counter; counter++; if (cacheKeyMutex != NULL) rmMutexUnlock(cacheKeyMutex); return(rval); } /* PRIVATE */ RMenum private_rmCacheInit(RMcontextCache **toCreate) { RMcontextCache *c; int nPrims, nImgs, nTextures; int i; c = (RMcontextCache *)malloc(sizeof(RMcontextCache)); memset(c, 0, sizeof(RMcontextCache)); /* prims, images, textures & associated cache keys */ nPrims = NUM_ITEMS_PER_PAGE; c->primDisplayListIDs = (GLuint *)malloc(sizeof(GLuint)*nPrims); c->primCacheKeys = (RMcacheKey *)malloc(sizeof(GLuint)*nPrims); c->numPrimDisplayListIDs = nPrims; c->numPrimCacheKeys = nPrims; for (i=0;iprimDisplayListIDs[i] = -1; c->primCacheKeys[i] = RM_MAKE_CACHEKEY(-1); } nImgs = NUM_ITEMS_PER_PAGE; c->imgDisplayListIDs = (GLuint *)malloc(sizeof(GLuint)*nImgs); c->imgCacheKeys = (RMcacheKey *)malloc(sizeof(GLuint)*nImgs); c->numImgDisplayListIDs = nImgs; c->numImgCacheKeys = nImgs; for (i=0;iimgDisplayListIDs[i] = -1; c->imgCacheKeys[i] = RM_MAKE_CACHEKEY(-1); } nTextures = NUM_ITEMS_PER_PAGE; c->textureIDs = (GLuint *)malloc(sizeof(GLuint)*nTextures); c->textureIDCacheKeys = (RMcacheKey *)malloc(sizeof(GLuint)*nTextures); c->textureDataCacheKeys = (RMcacheKey *)malloc(sizeof(GLuint)*nTextures); c->numTextureIDs = nTextures; c->numTextureIDCacheKeys = nTextures; c->numTextureDataCacheKeys = nTextures; for (i=0;itextureIDs[i] = -1; c->textureIDCacheKeys[i] = RM_MAKE_CACHEKEY(-1); c->textureDataCacheKeys[i] = RM_MAKE_CACHEKEY(-1); } /* font registry */ c->pipeFontRegistry = private_rmFontRegistryNew(); *toCreate = c; return (RM_CHILL); } /* PRIVATE */ RMenum private_rmCacheDelete(RMpipe *p, RMcontextCache *toDelete) { RMcontextCache *c; c = toDelete; private_rmCacheDeleteAllPrimitiveDisplayLists(c); private_rmCacheDeleteQuadrics(c); private_rmCacheDeleteAllImageDisplayLists(c); private_rmCacheDeleteAllTextures(c); /* prims, images, textures & associated cache keys */ free((void *)(c->primDisplayListIDs)); free((void *)(c->primCacheKeys)); free((void *)(c->imgDisplayListIDs)); free((void *)(c->imgCacheKeys)); free((void *)(c->textureIDs)); free((void *)(c->textureIDCacheKeys)); free((void *)(c->textureDataCacheKeys)); /* font registry */ private_rmFontRegistryDelete(p, c->pipeFontRegistry); free((void *)c); return (RM_CHILL); } /* PRIVATE */ void private_rmCacheFlush(RMcontextCache *toDelete) { extern RMcompMgrHdr *global_RMimagePool, *global_RMprimitivePool; extern RMcompMgrHdr *global_RMtexturePool; RMcontextCache *c; int nPrims, nImages, nTextures,i; /* * we could store the number of active prims, images & textures * in the RMpipe, but don't since our goal is to effectively * hide the many-pipes/renderers to one scene graph metaphor. */ /* * temp hack - we should traverse the pseudo linked list * in each RMcompMgrHdr structure to visit only RMprims, * RMimages and RMtextures that are resident in the scene graph. * what we do instead is charge through the entire pool of * all such objects. */ nPrims = global_RMprimitivePool->currentPoolSize; nImages = global_RMimagePool->currentPoolSize; nTextures = global_RMtexturePool->currentPoolSize; /* first, release resources associated with any existing display lists or textures. */ /* * Implementation/efficiency note: it may be the case that * some OpenGL implementations don't recycle display list/ * texture IDs, in which case, it would be possible to "run out" * after some number of cache flushes/rebuilds. We may want * to modify our flush strategy by *not* deleting display * lists and textures, and simply rebuilding them the next * time the i'th RMprimitive, RMimage and RMtexture needs * to have retained mode structures rebuilt. */ c = toDelete; if (RM_ASSERT(c,"private_rmCacheFlush() error: the input context cache is NULL!") == RM_WHACKED) return; /* * each of these loops releases any OpenGL resources that might * be used. note that we're re-initializing the entries to * default values that mean "empty", then just throwing the * memory away a little bit later. gotta do something with * those gigahertz CPUs. */ for (i=0;iprimDisplayListIDs[i]) == GL_TRUE) glDeleteLists(c->primDisplayListIDs[i], 1); c->primDisplayListIDs[i] = -1; c->primCacheKeys[i] = RM_MAKE_CACHEKEY(-1); } for (i=0;iimgDisplayListIDs[i]) == GL_TRUE) glDeleteLists(c->imgDisplayListIDs[i], 1); c->imgDisplayListIDs[i] = -1; c->imgCacheKeys[i] = RM_MAKE_CACHEKEY(-1); } for (i=0;itextureIDs[i]) == GL_TRUE) glDeleteTextures(1,&(c->textureIDs[i])); c->textureIDs[i] = -1; c->textureIDCacheKeys[i] = RM_MAKE_CACHEKEY(-1); c->textureDataCacheKeys[i] = RM_MAKE_CACHEKEY(-1); } /* now, free up the memory associated with the cache keys */ free((void *)c->primDisplayListIDs); free((void *)c->primCacheKeys); free((void *)c->imgDisplayListIDs); free((void *)c->imgCacheKeys); free((void *)c->textureIDs); free((void *)c->textureIDCacheKeys); free((void *)c->textureDataCacheKeys); free((void *)c); } /* PRIVATE */ RMenum private_rmCacheDeleteAllPrimitiveDisplayLists(RMcontextCache *c) { int i; /* * this routine will delete all display lists associated with * RMprimitive's in the RMpipe's context cache. */ /* * 7/4/01 w.bethel * the following code makes simplifying assumptions that * result in overkill: * 1. RM_COMPONENT_POOL_SIZE is the init size of the context * cache, not the real size as the component mgr pool grows * (which isn't yet implemented anyway). * 2. the following loop serially examines *all* primDisplayListIDs * rather than looking at only a subset. this is more expensive than * alternative approaches, but will do a more thorough job of * cleansing display lists across potentially disconnected * scene graph nodes. * 3. this routine assumes that you really want to blow away all * the display lists owned by an RMpipe. * * 8/29/02 - cleaned up code to accommodate dynamic object reallocs. */ for (i=0; i < c->numPrimDisplayListIDs; i++) { GLuint list = c->primDisplayListIDs[i]; if ((list != (unsigned int)(-1)) && (glIsList(list)==GL_TRUE)) { glDeleteLists(list,1); c->primDisplayListIDs[i] = -1; c->primCacheKeys[i] = RM_MAKE_CACHEKEY(-1); } } return RM_CHILL; } /* PRIVATE */ RMenum private_rmCacheDeleteAllTextures(RMcontextCache *c) { int i; /* * this routine will delete all texture object id's associated with * the RMpipe's context cache. */ /* * 7/4/01 w.bethel * the following code makes simplifying assumptions that * result in overkill: * 1. RM_COMPONENT_POOL_SIZE is the init size of the context * cache, not the real size as the component mgr pool grows * (which isn't yet implemented anyway). * 2. the following loop serially examines *all* textureIDs * rather than looking at only a subset. this is more expensive than * alternative approaches, but will do a more thorough job of * cleansing textures across potentially disconnected * scene graph nodes. * 3. this routine assumes that you really want to blow away all * the textures owned by an RMpipe. * * 8/29/02 - wes. cleaned up to accommodate dynamic object reallocs. */ for (i=0; i < c->numTextureIDs; i++) { GLuint objID = c->textureIDs[i]; if ((objID != (GLuint)(0)) && (glIsTexture(objID)==GL_TRUE)) { glDeleteTextures(1, &objID); c->textureIDs[i] = -1; c->textureIDCacheKeys[i] = RM_MAKE_CACHEKEY(-1); c->textureDataCacheKeys[i] = RM_MAKE_CACHEKEY(-1); } } return RM_CHILL; } /* PRIVATE */ RMenum private_rmCacheDeleteAllImageDisplayLists(RMcontextCache *c) { int i; /* * this routine will display list id's associated with * RMimages in the RMpipe's context cache. */ /* * 7/4/01 w.bethel * the following code makes simplifying assumptions that * result in overkill: * 1. RM_COMPONENT_POOL_SIZE is the init size of the context * cache, not the real size as the component mgr pool grows * (which isn't yet implemented anyway). * 2. the following loop serially examines *all* RMimage display list ids * rather than looking at only a subset. this is more expensive than * alternative approaches, but will do a more thorough job of * cleansing display list ids across potentially disconnected * scene graph nodes. * 3. this routine assumes that you really want to blow away all * the textures owned by an RMpipe. * * 8/29/02 - wes. updated to accommodate dynamic object reallocs. */ for (i=0; i < c->numImgDisplayListIDs; i++) { GLuint list = c->imgDisplayListIDs[i]; if ((list != (unsigned int)(-1)) && (glIsList(list)==GL_TRUE)) { glDeleteLists(list,1); c->imgDisplayListIDs[i] = RM_MAKE_CACHEKEY(-1); c->imgCacheKeys[i] = (RMcacheKey)(-1); } } return RM_CHILL; } /* PRIVATE */ int private_rmCacheComputeNumberNewPages(int oldNItems, int numItemsPerPage, int newIndx) { /* * input: oldNItems - says how many items we have already, * nItemsPerPage - number of items per page in realloc * newIndx - index, or "item number" to meet or exceed. * * compute and return: number of pages, at newItemsPerPage, must be * allocated in order to accommodate an items positioned at "newIndx." */ int i = oldNItems / numItemsPerPage; int newTop = oldNItems; while (newIndx >= newTop) { newTop += numItemsPerPage; i++; } return i; /* return a count of new pages needed */ } /* EOF */