/* * 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: rmimage.c,v 1.8 2005/06/26 18:50:25 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.8 $ * $Log: rmimage.c,v $ * Revision 1.8 2005/06/26 18:50:25 wes * Documentation updates. * * Revision 1.7 2005/05/15 15:22:11 wes * Update inline docs for rmImageMirror. * * Revision 1.6 2005/05/06 16:36:53 wes * Update docs to reflect that 1D images are supported. Only a couple * of minor code tweaks were needed. * * Revision 1.5 2005/02/19 16:32:31 wes * Distro sync and consolidation. * Add code to set "bytes per component" field of an RMimage. * * Revision 1.4 2005/01/23 17:04:03 wes * Copyright update to 2005. * * Revision 1.3 2004/01/16 16:45:12 wes * Updated copyright line for 2004. * * Revision 1.2 2003/02/02 02:07:15 wes * Updated copyright to 2003. * * Revision 1.1.1.1 2003/01/28 02:15:23 wes * Manual rebuild of rm150 repository. * * Revision 1.10 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.9 2002/12/04 14:50:33 wes * Cleanup SGI compiles. * * Revision 1.8 2002/04/30 19:28:17 wes * rmImageSetVismap() now takes NULL for the vismap parameter. This change * allows applications to "clear" any vis colormap associated with an * RMimage, and is also used to fix a memory leak bug associated with * rmImageDelete. * * Revision 1.7 2001/10/15 01:38:53 wes * Bugs in 4-byte alignment code for W32 rmimages fixed. * * Revision 1.5 2001/05/28 23:29:29 wes * Fixed nbytesPerScanline computation bug in rmImageNew. * * Revision 1.4 2001/03/31 17:12:38 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" /* * ---------------------------------------------------- * @Name rmImageNew @pstart RMimage * rmImageNew (int ndims, int width, int height, int depth, RMenum formatEnum, RMenum typeEnum, RMenum copyFlag) @pend @astart int ndims - integer value specifying the number of dimensions in the new image. May be a value of 2 for regular images, 3 for 3D volumes, or 1 for 1D (texture) images (input). int width, height - integer values specifying the width and height of the new image (input). int depth - integer value specifying the size of the 3rd image dimension for 3D images. When "ndims" is 1 or 2, this parameter is ignored (input). RMenum formatEnum - an RMenum value specifying the pixel format for the new image. Use one of the following values (input): RM_IMAGE_ALPHA - alpha-only pixels, one component per pixel. RM_IMAGE_LUMINANCE - luminance only pixels, one component per pixel. RM_IMAGE_LUMINANCE_ALPHA - each pixel is considered to be a luminance-alpha pair, two components per pixel. Pixel data organized as LALALA... RM_IMAGE_RGB - each pixel is considered to be an RGB 3-tuple, organized as RGBRGB... RM_IMAGE_RGBA - each pixel is considered to be an RGBA 4-tuple, organized as RGBARGBA... RM_IMAGE_DEPTH - identifies the pixel data as depth buffer pixels. Each pixel consists of a single component. RMenum typeEnum - an RMenum value specifying the type of each pixel component. Use one of the following enumerators (input): RM_UNSIGNED_BYTE, RM_FLOAT, RM_SHORT, or RM_UNSIGNED_SHORT. RMenum copyFlag - an RMenum value specifying whether or not RM will make a copy of the pixel data. Use one of RM_COPY_DATA or RM_DONT_COPY_DATA (input). @aend @dstart Creates a new RMimage object according to the caller's specifications, and returns an RMimage handle to the caller upon success, or NULL upon failure. Once created, the size and pixel format/type of an RMimage object may not be changed by the application. However, the pixel storage mode may change over the lifetime of the RMimage object. Win32 peculiarities: due to bugs in the Win32 OpenGL, all pixel data in an RMimage object is padded to 4-byte boundaries on a per-scanline basis. This will become significant when transferring pixel data to the RMimage object. Use the routine rmImageGetBytesPerScanline to obtain the computed number of bytes per scanline (that value is computed by RM by rmImageNew and remains constant for the lifetime of the RMimage object). When copyFlag is set to RM_COPY_DATA, rmImageNew creates a pixel buffer internal to the RMimage object. When set to RM_DONT_COPY_DATA, no pixel buffer is created. However, the bytes-per-scanline value is computed and assigned to the new RMimage object. Important! Applications must honor the bytes-per-scanline attribute of the RMimage object, otherwise the application's notion of pixel data organization will not jibe with RM's notion, leading potentially to garbage images on screen (at best) to possible seg faults (at worst). @dend * ---------------------------------------------------- */ RMimage * rmImageNew (int ndims, int width, int height, int depth, RMenum format_enum, /* RM_IMAGE_RGB, etc. */ RMenum type_enum, /* RM_UNSIGNED_BYTE or RM_FLOAT */ RMenum copy_flag) { int elements; int nbytes_per_component, nbytes_per_scanline, nbytes_for_pixeldata; RMimage *t; int save; t = private_rmImageNew(); save = t->compListIndx; memset(t, 0, sizeof(RMimage)); t->compListIndx = save; if (t == NULL) { rmError("rmImageNew() error: unable to allocate a new RMimage object."); return(NULL); } private_rmImageSetNDims(t, ndims); private_rmImageSetType(t, type_enum); private_rmImageSetWidth(t, width); private_rmImageSetHeight(t, height); if ((ndims == 2) || (ndims == 1)) depth = 1; private_rmImageSetDepth(t, depth); if ((elements = private_rmImageGetNumElements(format_enum)) == RM_WHACKED) { rmError("rmImage3DNew() error: unsupported image format requested."); return(NULL); } private_rmImageSetElements(t, elements); nbytes_per_component = private_rmImageNumComponentBytes(type_enum); private_rmImageSetBytesPerComponent(t, nbytes_per_component); nbytes_per_scanline = elements * width * nbytes_per_component; #ifdef RM_WIN { int extraBytes; extraBytes = nbytes_per_scanline % 4; if (extraBytes != 0) { extraBytes = 4 - extraBytes; nbytes_per_scanline += extraBytes; #if 0 while (extraBytes > 0) { nbytes_per_scanline++; extraBytes--; } #endif } } #endif nbytes_for_pixeldata = nbytes_per_scanline * height * depth; private_rmImageSetBytesPerScanline(t, nbytes_per_scanline); t->pbsize = nbytes_for_pixeldata; private_rmImageSetFormat(t, format_enum); rmImageSetPixelZoom(t, 1.0F, 1.0F); /* default */ rmImageSetScale(t, 1.0F); rmImageSetBias(t, 0.0F); if (copy_flag == RM_COPY_DATA) { t->pixeldata = (unsigned char *)malloc(sizeof(unsigned char) * nbytes_for_pixeldata); if (t == NULL) { char buf[128]; sprintf(buf,"rmImage3DNew() error: unable to allocated %d bytes of voxel data.", nbytes_for_pixeldata); rmError(buf); return(NULL); } memset(t->pixeldata, 0, sizeof(unsigned char) * nbytes_for_pixeldata); private_rmImageSetCopyFlag(t, RM_COPY_DATA); } else { private_rmImageSetCopyFlag(t, RM_DONT_COPY_DATA); t->pixeldata = NULL; } #if 0 /* 10/5/2000 - all RMimage display list code removed during SBIR work */ private_rmImageSetDisplayList(t, -1); #endif return(t); } /* * ---------------------------------------------------- * @Name rmImageDup @pstart RMimage * rmImageDup (const RMimage *toDuplicate) @pend @astart const RMimage *toDuplicate - a handle to an RMimage object (input). @aend @dstart Use this routine to create a duplicate of an RMimage object. Returns a handle to the new object upon success, or NULL upon failure. A new RMimage object is created that is an exact duplicate of the input object. When the pixel data is under the purview of RM_COPY_DATA, a copy of the pixel data from the source RMimage is created and placed into the new RMimage object's pixel buffer. Otherwise (RM_DONT_COPY_DATA), the handle to the application pixel data is copied to the new RMimage object, along with the handle to the application callback used to free pixel data. The RMvisMap object, if present in source, is duplicated and copied to the new object. RMimage object's do not share data management of RMvisMap objects with the application. All size and other configuration information from the source is used to create the new object. Pixel zoom, bias and scale are also copied from the source to the new object. @dend * ---------------------------------------------------- */ RMimage * rmImageDup (const RMimage *src) { unsigned char *s, *d; float xz, yz; RMenum copy_flag; RMimage *dst; RMvisMap *vmap = NULL; if (RM_ASSERT(src, "rmImageDup() error: input RMimage is NULL.") == RM_WHACKED) return(NULL); copy_flag = private_rmImageGetCopyFlag(src); /* make a new image which has all the config information present in the old image */ dst = rmImageNew(private_rmImageGetNDims(src), private_rmImageGetWidth(src), private_rmImageGetHeight(src), private_rmImageGetDepth(src), private_rmImageGetFormat(src), private_rmImageGetType(src), private_rmImageGetCopyFlag(src)); if (dst == NULL) return(NULL); rmImageGetPixelZoom(src, &xz, &yz); rmImageSetPixelZoom(dst, xz, yz); rmImageGetScale(src, &xz); rmImageSetScale(dst, xz); rmImageGetBias(src, &xz); rmImageSetBias(dst, xz); s = rmImageGetPixelData(src); d = rmImageGetPixelData(dst); if (private_rmImageGetCopyFlag(src) == RM_COPY_DATA) memcpy((void *)d, (void *)s, sizeof(unsigned char)*src->pbsize); else { dst->pixeldata = s; dst->appfreefunc = src->appfreefunc; } if (rmImageGetVismap(src, &vmap) == RM_CHILL) { rmImageSetVismap(dst, vmap); rmVismapDelete(vmap); } return(dst); } /* * ---------------------------------------------------- * @Name rmImageDelete @pstart RMenum rmImageDelete (RMimage *toDelete) @pend @astart RMimage *toDelete - a handle to an RMimage object. @aend @dstart Releases resources associated with an RMimage object (the opposite of rmImageNew). Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmImageDelete (RMimage *r) { if (RM_ASSERT(r, "rmImageDelete() error: input RMimage is NULL.") == RM_WHACKED) return(RM_WHACKED); return(private_rmImageDelete(r)); #if 0 /* this code goes away with the new component manager */ if (private_rmImageGetCopyFlag(r) == RM_COPY_DATA) free((void *)(r->pixeldata)); else { void (*appfunc)(void *); appfunc = r->appfreefunc; if (appfunc != NULL) (*appfunc)((void *)(r->pixeldata)); } free(r); return(RM_CHILL); #endif } /* * ---------------------------------------------------- * @Name rmImageSetPixelData @pstart RMenum rmImageSetPixelData (RMimage *toModify, void *pixelData, RMenum copyEnum, void (*appFreeFunc)(void *)) @pend @astart RMimage *toModify - a handle to an RMimage object (modified). void *pixelData - a handle to the raw pixel data supplied by the caller. The contents of this buffer will become the pixel data for the RMimage object (input). RMenum copyEnum - an RMenum value specifying whether or not RM should make a copy of this data. Must be either RM_COPY_DATA or RM_DONT_COPY_DATA. void (*appFreeFunc)(void *) - a handle to an application callback used to free the pixel data when the RMimage object is deleted. When copyEnum is RM_COPY_DATA, this parameter is ignored. @aend @dstart Assigns raw pixel data to an RMimage object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The expected size of the pixel buffer is: (bytes_per_scanline)*height*depth * num_pixel_components The type of each pixel component may be obtained with rmImageGetType. The RMimage object's internal pixel buffer created with rmImageNew using RM_COPY_DATA should be considered READ ONLY. It is possible for applications to write directly into this pixel buffer, but this practice is highly discouraged. The reason is that RM will attempt to cache the RMimage object pixel buffer into OpenGL display lists wherever possible, and is capable of detecting changes to the pixel buffer (implying the need to refresh the display list data) only when the RM interface is used to update the pixel data (rmImageSetPixelData). In order to avoid multiple copies of pixel data in memory, use RM_DONT_COPY_DATA when creating the RMimage object. In that case, the application manages the pixel data. A call to rmImageSetPixelData will result in a pointer copy (not a block pixel copy), and will correctly cause any dependent display lists to be refreshed. When rmImageSetPixelData is called using RM_COPY_DATA, RM makes a copy of the pixel buffer and stores it in the RMimage object. When called with RM_DONT_COPY_DATA, RM copies only the pointer to the pixel data. When using RM_DONT_COPY_DATA, applications must provide a callback that will be invoked when the RMimage object is deleted with rmImageDelete. The application callback will be passed a single parameter, a handle to the raw pixel data cast to a void *. @dend * ---------------------------------------------------- */ RMenum rmImageSetPixelData (RMimage *ri, void *pixeldata, RMenum copy_enum, void (*appfreefunc)(void *)) { /* * validity checking: * 1. input image & pixeldata must not be null * 2. assert copy_enum == RM_DONT_COPY_DATA && appfreefunc != NULL */ RMenum old_copy_flag; if (RM_ASSERT(ri, "rmImageSetPixelData() error: NULL input RMimage.") == RM_WHACKED) return(RM_WHACKED); if ((copy_enum == RM_DONT_COPY_DATA) && (appfreefunc == NULL)) { rmError("rmImageSetPixelData() error: when using RM_DONT_COPY_DATA, you must supply a function which RM will call to free the image buffer. RM will not call this function until you delete the RMnode that contains the image (texture, sprite primitive, etc.)"); return(RM_WHACKED); } /* * we need to check for the case in which the image was created with * RM_COPY_DATA, but now data is being set with RM_DONT_COPY_DATA. this * may be a potential problem.. */ old_copy_flag = private_rmImageGetCopyFlag(ri); if (old_copy_flag != copy_enum) /* a problem */ { if (old_copy_flag == RM_COPY_DATA) /* & copy_flag == RM_DONT_COPY */ free((void *)(rmImageGetPixelData(ri))); /* free it */ else /* old is DONT_COPY, new is COPY, malloc buffer */ { unsigned char *c; void (*appfunc)(void *); /* call the app free func, if defined, to free up the old buffer */ appfunc = ri->appfreefunc; if (appfunc != NULL) (*appfunc)((void *)(rmImageGetPixelData(ri))); c = (unsigned char *)malloc(sizeof(unsigned char) * ri->pbsize); ri->pixeldata = c; } } /* else, no problem */ private_rmImageSetCopyFlag(ri, copy_enum); if (copy_enum == RM_COPY_DATA) { memcpy((void *)(ri->pixeldata), pixeldata, ri->pbsize); } else { ri->pixeldata = (unsigned char *)pixeldata; ri->appfreefunc = appfreefunc; } #if 0 /* 10/5/200 - should be replaced with context cache stuff */ private_rmImageSetDirtyFlag(ri, RM_TRUE); /* what's this for ? */ #endif return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmImageGetPixelData @pstart void * rmImageGetPixelData (const RMimage *toQuery) @pend @astart const RMimage *toQuery @aend @dstart Returns to the caller a handle to the raw pixel data inside an RMimage object, cast to a void * upon success. Otherwise, returns NULL. The size of the returned pixel buffer is: (bytes_per_scanline)*height*depth * num_pixel_components The type of each pixel component may be obtained with rmImageGetType. The RMimage object's internal pixel buffer created with rmImageNew using RM_COPY_DATA should be considered READ ONLY. It is possible for applications to write directly into this pixel buffer, but this practice is highly discouraged. The reason is that RM will attempt to cache the RMimage object pixel buffer into OpenGL display lists wherever possible, and is capable of detecting changes to the pixel buffer (implying the need to refresh the display list data) only when the RM interface is used to update the pixel data (rmImageSetPixelData). In order to avoid multiple copies of pixel data in memory, use RM_DONT_COPY_DATA when creating the RMimage object. In that case, the application manages the pixel data. A call to rmImageSetPixelData will result in a pointer copy (not a block pixel copy), and will correctly cause any dependent display lists to be refreshed. @dend * ---------------------------------------------------- */ void * rmImageGetPixelData (const RMimage *ri) { if (RM_ASSERT(ri, "rmImageGetPixelData() error: input RMimage is NULL") == RM_WHACKED) return(NULL); return((void *)(ri->pixeldata)); } /* * ---------------------------------------------------- * @Name rmImageGetBytesPerScanline @pstart unsigned int rmImageGetBytesPerScanline (const RMimage *toQuery) @pend @astart const RMimage *toQuery - a handle to an RMimage object (input). @aend @dstart Returns to the caller the computed number of bytes per scanline of image data. Some OpenGL implementations silently require that all image data is padded to 4-byte boundaries on a per-scanline basis. This read-only value, bytes-per-scanline, is computed by rmImageNew at RMimage creation time and remains constant for the lifetime of the RMimage object. Note that there is no corresponding rmImageSetBytesPerScanline routine. The bytes per scanline of an RMimage object is established at the time the RMimage object is created with rmImageNew and remains immutable for the lifetime of the object. See also rmImageGetImageSize. @dend * ---------------------------------------------------- */ unsigned int rmImageGetBytesPerScanline (const RMimage *src) { if (RM_ASSERT(src, "rmImageGetBytesPerScanline() error: input RMimage pointer is NULL.") == RM_WHACKED) return(0); return(private_rmImageGetBytesPerScanline(src)); } /* * ---------------------------------------------------- * @Name rmImageGetCopyFlag @pstart RMenum rmImageGetCopyFlag (const RMimage *toQuery) @pend @astart const RMimage *toQuery - a handle to an RMimage object object (input). @aend @dstart Returns to the caller an RMenum value indicating the RMimage object's notion of who manages the pixel data. Either RM_COPY_DATA or RM_DONT_COPY_DATA will be returned, indicating that RM manages the data or the application manages the data, respectively. If the input RMimage is NULL, then RM_WHACKED is returned. There is no corresponding rmImageSetCopyFlag routine. The copy flag attribute is established at the time the RMimage is created with rmImageNew is called and is immutable for the lifetime of the object. @dend * ---------------------------------------------------- */ RMenum rmImageGetCopyFlag (const RMimage *ri) { if (RM_ASSERT(ri, "rmImageGetCopyFlag() error: the input RMimage is NULL.") == RM_WHACKED) return(RM_WHACKED); return(private_rmImageGetCopyFlag(ri)); } /* * ---------------------------------------------------- * @Name rmImageGetType @pstart RMenum rmImageGetType (const RMimage *toQuery) @pend @astart const RMimage *toQuery @aend @dstart Returns to the caller an RMenum value indicating the RMimage pixel type. Upon success, the RMenum value will be one of the RM pixel types, such as RM_UNSIGNED_BYTE, RM_FLOAT, RM_SHORT, or RM_UNSIGNED_SHORT. Otherwise, RM_WHACKED is returned. There is no corresponding rmImageSetType routine. The pixel type of an RMimage object is established at the time the RMimage object is created with rmImageNew, and remains immutable for the lifetime of the object. @dend * ---------------------------------------------------- */ RMenum rmImageGetType (const RMimage *img) { if (RM_ASSERT(img, "rmImageGetType() error: input RMimage pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); return(private_rmImageGetType(img)); } /* * ---------------------------------------------------- * @Name rmImageGetFormat @pstart RMenum rmImageGetFormat (const RMimage *toQuery) @pend @astart const RMimage *toQuery @aend @dstart Returns to the caller an RMenum value indicating the pixel format of the RMimage object. Upon success, the return value will be one of the RM pixel format enumerators, such as RM_IMAGE_ALPHA, RM_IMAGE_LUMINANCE, RM_IMAGE_LUMINANCE_ALPHA, RM_IMAGE_RGB, RM_IMAGE_RGBA or RM_IMAGE_DEPTH. Upon failure, RM_WHACKED is returned to the caller. Note that there is no corresponding rmImageSetFormat routine. The pixel format of an RMimage object is established at the time the RMimage object is created with rmImageNew and remains immutable for the lifetime of the object. @dend * ---------------------------------------------------- */ RMenum rmImageGetFormat (const RMimage *img) { if (RM_ASSERT(img, "rmImageGetFormat() error: input RMimage pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); return(private_rmImageGetFormat(img)); } /* * ---------------------------------------------------- * @Name rmImageGetImageSize @pstart RMenum rmImageGetImageSize (const RMimage *toQuery, int *returnNDims, int *returnWidth, int *returnHeight, int *returnDepth, int *returnElements, int *returnBytesPerScanline) @pend @astart const RMimage *toQuery - a handle to an RMimage object to query (input). int *returnNDims, *returnWidth, *returnHeight, *returnDepth, *returnElements, *returnBytesPerScanline - handles to integers supplied by the caller. Parameters from the RMimage object will be copied into these caller-supplied memory areas. Specifying NULL for one or more of these values is permissible. @aend @dstart Use rmImageGetImageSize to obtain configuration information about an RMimage object. If not NULL, upon return, "returnNDims" will contain the number of dimensions in the RMimage object, either 2 or 3. If not NULL, upon return, "returnWidth", "returnHeight" and "returnDepth" will contain the width, height and depth of the RMimage object. When the dimensionality of the RMimage object is 2, "returnDepth" will be set to 1. If not NULL, upon return, "returnElements" will contain the number of components per pixel. For example, if the pixel format is RM_IMAGE_RGB, "returnElements" will contain 3. If not NULL, upon return, "returnBytesPerScanline" will contain the value computed by RM indicating the actual number of bytes per scanline in the pixel buffer. That value is computed to honor scanline padding requirements of some OpenGL implementations. This read-only value is computed by rmImageNew. Returns RM_CHILL upon success, or RM_WHACKED upon failure. Note that there is no corresponding rmImageSetImageSize routine. The size of an RMimage object is established at the time the RMimage object is created with rmImageNew and remains immutable for the lifetime of the object. @dend * ---------------------------------------------------- */ RMenum rmImageGetImageSize (const RMimage *ri, int *ret_ndims, int *ret_w, int *ret_h, int *ret_d, int *ret_e, int *bytes_per_scanline) { if (RM_ASSERT(ri, "rmImageGetImageSize() error: input RMimage is NULL.") == RM_WHACKED) return(RM_WHACKED); /* * we assume that the caller may or may not be interested in all the * parms, therefore it's not an error for some of the parms to be NULL. */ if (ret_ndims != NULL) *ret_ndims = private_rmImageGetNDims(ri); if (ret_w != NULL) *ret_w = private_rmImageGetWidth(ri); if (ret_h != NULL) *ret_h = private_rmImageGetHeight(ri); if (ret_d != NULL) *ret_d = private_rmImageGetDepth(ri); if (ret_e != NULL) *ret_e = private_rmImageGetElements(ri); if (bytes_per_scanline != NULL) *bytes_per_scanline = private_rmImageGetBytesPerScanline(ri); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmImageSetPixelZoom @pstart RMenum rmImageSetPixelZoom (RMimage *toModify, float xzoom, float yzoom) @pend @astart RMimage *toModify - a handle to an RMimage object (modified). float xzoom, yzoom - floating point values used to specify the x- and y-axis pixel replication, or zoom, factors (input). @aend @dstart This routine is used to set the x- and y-axis pixel zoom parameters of an RMimage object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. In OpenGL, image-based data is usually drawn pixel-for-pixel, so that one pixel in the source image maps to one pixel on the screen. That mapping can be modified by changing the "pixel zoom". Setting the x-axis pixel zoom to 2.0 will cause each input pixel to map to two on-screen pixels. Setting the x-axis pixel zoom to 0.5 will cause each two pixels of input to map to one pixel of output. Pixel zoom operations apply to RM_SPRITE primitives, and background image tiles. The pixel zoom values are not used when the RMimage object is the source of pixel data in an RMtexture (used in texture mapping). @dend * ---------------------------------------------------- */ RMenum rmImageSetPixelZoom (RMimage *ri, float xzoom, float yzoom) { if (RM_ASSERT(ri, "rmImageSetPixelZoom() error: input RMimage pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); ri->xzoom = xzoom; ri->yzoom = yzoom; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmImageGetPixelZoom @pstart RMenum rmImageGetPixelZoom (const RMimage *toQuery, float *returnXZoom, float *returnYZoom) @pend @astart RMimage *toQuery - a handle to an RMimage object that will be queried by this routine (input). float *returnXZoom, *returnYZoom - handles to floating point values that will contain the x- and y-axis pixel zoom factors of the RMimage object toQuery (modified). @aend @dstart This routine is used to obtain the x- and y-axis pixel zoom parameters of an RMimage object. The pixel zoom factors are copied into caller-supplied memory. Returns RM_CHILL upon success, or RM_WHACKED upon failure. In OpenGL, image-based data is usually drawn pixel-for-pixel, so that one pixel in the source image maps to one pixel on the screen. That mapping can be modified by changing the "pixel zoom". Setting the x-axis pixel zoom to 2.0 will cause each input pixel to map to two on-screen pixels. Setting the x-axis pixel zoom to 0.5 will cause each two pixels of input to map to one pixel of output. Pixel zoom operations apply to RM_SPRITE primitives, and background image tiles. The pixel zoom values are not used when the RMimage object is the source of pixel data in an RMtexture (used in texture mapping). @dend * ---------------------------------------------------- */ RMenum rmImageGetPixelZoom (const RMimage *ri, float *xzoom, float *yzoom) { if (RM_ASSERT(ri, "rmImageGetPixelZoom() error: the input RMimage object is NULL.") == RM_WHACKED) return(RM_WHACKED); if (RM_ASSERT(xzoom, "rmImageGetPixelZoom() error: the input xzoom parameter pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); if (RM_ASSERT(yzoom, "rmImageGetPixelZoom() error: the input yzoom parameter pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); *xzoom = ri->xzoom; *yzoom = ri->yzoom; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmImageSetVismap @pstart RMenum rmImageSetVismap (RMimage *toModify, const RMvisMap *vismap) @pend @astart RMimage *toModify - a handle to an RMimage object (modified). const RMvisMap *vismap - a handle to an RMvisMap object (input). @aend @dstart Use this routine to set, or delete, the colormap associated with an RMimage object. This routine will return RM_CHILL upon success, or RM_WHACKED upon failure. A copy of the caller's RMvisMap object is created inside this routine, and assigned to the RMimage object. If the input RMvisMap parameter is NULL, any RMvisMap associated with the RMimage object will be deleted. Application changes to the application RMvisMap object made after this call will not affect the RMimage object. The colormap is used at pixel expansion time within the OpenGL pixel pipeline. Inside OpenGL, application pixel components are first converted to the range [0..1] using an algorithm that is a function of the source pixel type (byte, short, etc.). Next, pixels are converted from the source pixel format (luminance-alpha, RGB, etc.) into RGBA. Next, each pixel component is multiplied by the "scale" factor, then "bias" is added. Finally, each pixel component undergoes a color->color lookup. This routine is used to control that color->color lookup. Pixel scale, bias and color lookup operations are applied to all imaging operations, including both pixel draws and during transfer of pixel data to texture memory. Therefore, this routine may be used to perform false-coloring of scalar image data either for direct display using an RM_SPRITE primitive or in the form of texture-mapping. Note that scale and bias parameters operate on pixel data that has been converted to the range [0..1] according to a type-specific algorithm. Source pixels that are in floating point are not converted, but are clamped to the range [0..1]. Clamping occurs late in the pixel pipeline, only after scale, bias and color lookup have been performed. False coloring with the RMvisMap object is implemented by manipulating OpenGL pixel transfer modes with glPixelTransferi(). January 2000 - RMvisMap objects contain a transfer function. The transfer function is effectively ignored during the false coloring process inside the OpenGL pixel pipeline. Use the scale and bias attributes of the RMimage object to implement a transfer function. However, the RMvisMap object's transfer function is used inside the RMV visualization routines. April 2002 - NULL is now permitted as a parameter for the vismap. this will permit applications to "clear" the vismap associated with an RMimage object. @dend * ---------------------------------------------------- */ RMenum rmImageSetVismap (RMimage *dst, const RMvisMap *vismap) { if (RM_ASSERT(dst, "rmImageSetVismap() error: input RMimage object is NULL") == RM_WHACKED) return(RM_WHACKED); if (dst->vismap) { rmVismapDelete(dst->vismap); dst->vismap = NULL; } if (vismap != NULL) dst->vismap = rmVismapDup(vismap); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmImageGetVismap @pstart RMenum rmImageGetVismap (const RMimage *toQuery, RMvisMap **vismapReturn) @pend @astart const RMimage *toQuery - a handle to an RMimage object (input). RMvisMap **vismapReturn - a handle to an RMvisMap pointer (modified). @aend @dstart Use this routine to set the obtain a copy of the colormap associated with an RMimage object. If a RMvisMap object exists inside the RMimage object, a copy of the RMvisMap object will be created and returned to the caller, and RM_CHILL will be returned. Otherwise, RM_WHACKED is returned. The RMvisMap object returned to the caller is a duplicate of the one inside the RMimage object. Application changes to the returned RMvisMap will not affect the RMimage object. When applications are finished with the returned RMvisMap object, resources should be freed with rmVismapDelete(). The colormap is used at pixel expansion time within the OpenGL pixel pipeline. Inside OpenGL, application pixel components are first converted to the range [0..1] using an algorithm that is a function of the source pixel type (byte, short, etc.). Next, pixels are converted from the source pixel format (luminance-alpha, RGB, etc.) into RGBA. Next, each pixel component is multiplied by the "scale" factor, then "bias" is added. Finally, each pixel component undergoes a color->color lookup. This routine is used to control that color->color lookup. Pixel scale, bias and color lookup operations are applied to all imaging operations, including both pixel draws and during transfer of pixel data to texture memory. Therefore, this routine may be used to perform false-coloring of scalar image data either for direct display using an RM_SPRITE primitive or in the form of texture-mapping. Note that scale and bias parameters operate on pixel data that has been converted to the range [0..1] according to a type-specific algorithm. Source pixels that are in floating point are not converted, but are clamped to the range [0..1]. Clamping occurs late in the pixel pipeline, only after scale, bias and color lookup have been performed. False coloring with the RMvisMap object is implemented by manipulating OpenGL pixel transfer modes with glPixelTransferi(). January 2000 - RMvisMap objects contain a transfer function. The transfer function is effectively ignored during the false coloring process inside the OpenGL pixel pipeline. Use the scale and bias attributes of the RMimage object to implement a transfer function. However, the RMvisMap object's transfer function is used inside the RMV visualization routines. @dend * ---------------------------------------------------- */ RMenum rmImageGetVismap (const RMimage *toQuery, RMvisMap **vismapReturn) { RMenum rstat; if ((RM_ASSERT(toQuery, "rmImageGetVismap() error: input RMimage object is NULL") == RM_WHACKED) || (RM_ASSERT(vismapReturn, "rmImageGetVismap() error: input vismap is NULL.") == RM_WHACKED)) return(RM_WHACKED); if (toQuery->vismap) { *vismapReturn = rmVismapDup(toQuery->vismap); rstat = RM_CHILL; } else rstat = RM_WHACKED; return(rstat); } /* * ---------------------------------------------------- * @Name rmImageMirror @pstart RMenum rmImageMirror (RMimage *toMirror, RMenum mirrorAxisEnum) @pend @astart RMimage *toMirror - a handle to an RMimage object (modified). RMenum mirrorAxisEnum - an RMenum value specifying along which axis mirroring will occur (input). @aend @dstart This routine is used to perform reflection of pixel data in an RMimage object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The parameter mirrorAxisEnum should be either RM_IMAGE_MIRROR_HEIGHT to flip the image along the height axis (exchanging the top and bottom), or RM_IMAGE_MIRROR_WIDTH to flip the image along the horizontal axis (exchanging left and right). May 2005 - at this time, this routine will operate only on 2D images. @dend * ---------------------------------------------------- */ RMenum rmImageMirror (RMimage *toMirror, RMenum mirrorAxisEnum) { int ndim; if (RM_ASSERT(toMirror,"rmImageMirror() error: the input RMimage object is NULL. ") == RM_WHACKED) return(RM_WHACKED); rmImageGetImageSize(toMirror, &ndim, NULL, NULL, NULL, NULL, NULL); if (ndim != 2) { rmWarning(" rmImageMirror() warning: can handle only 2D images at this time. "); return(RM_WHACKED); } if (mirrorAxisEnum == RM_IMAGE_MIRROR_HEIGHT) return(private_rmImage2DMirrorVertical(toMirror)); else /* assume RM_IMAGE_MIRROR_WIDTH */ return(private_rmImage2DMirrorHorizontal(toMirror)); } /* * ---------------------------------------------------- * @Name rmImageResize @pstart RMenum rmImageResize (const RMimage *src, RMimage *dst, RMenum hardwareEnum, RMpipe *hwPipe) @pend @astart const RMimage *src - a handle to an RMimage object. Used as the source image in an image resize operation. RMimage *dst - a handle to an RMimage object. The resized image will be placed into this object. RMenum hardwareEnum - an RMenum value specifying whether or not to use the graphics hardware for performing the pixel resize operation. Must be either RM_HARDWARE or RM_SOFTWARE. RMpipe *hwPipe - a handle to an RMpipe (input). This parameter must be non-NULL whenever the hardwareEnum parameter is set to RM_HARDWARE, and must refer to a fully initialized RMpipe. When RM_SOFTWARE image resizing is specified, this parameter is ignored, and may be set to NULL by the caller. @aend @dstart Use this routine to scale an image. The source image is scaled to match the dimensions specified by the destination image. Thus, the destination image must have been first created with rmImageNew, implying that it's size, pixel format and type attributes have been set. This routine does not create a new image. This routine is useful for resizing images to be an even power of 2 in size, as required for OpenGL texture mapping. Returns RM_CHILL upon success, or RM_WHACKED upon failure. When hardwareEnum is set to RM_SOFTWARE, the routine gluScaleImage is used to perform the image resize operation, therefore it is a software operation. Note that gluScaleImage queries the underlying OpenGL for information about unpacklength. Therefore, OpenGL must be fully initialized prior to making a call to rmImageResize using either RM_HARDWARE or RM_SOFTWARE. When hardwareEnum is set to RM_HARDWARE, the underlying graphics hardware is used to perform the resizing operation. This is accomplished by writing pixels to the framebuffer using appropriate pixel zoom values, then reading the image back into the RMimage pixel data memory. In many cases, use of RM_HARDWARE will greatly accelerate image resizing operations, such as resizing a giga-scale volume down to something more manageable. A valid, initialized RMpipe is required in the hwPipe parameter when using RM_HARDWARE image resize methods. During RM_HARDWARE image resize operations, pixels are written into the back buffer, then read back. After the readback, a swapbuffers is performed to provide visual feedback. Since the backbuffer is used for write/read operations, the display window need not be on top in stacking order. When using RM_HARDWARE, the size of the window used by the hwPipe RMpipe must be greater than or equal in size to the size of the destination image dimensions. If this condition does not hold, an error message is issued, and RM_WHACKED is returned. Note about RM_HARDWARE and using RM_PIPE_MULTISTAGE_PARALLEL RMpipe processing modes: use of RM_HARDWARE in conjunction with RM_PIPE_MULTISTAGE_PARALLEL is guaranteed to work only if the image resize operation is performed before the first rmFrame() call. After the first rmFrame() call, an image resize operation that uses RM_HARDWARE on an RMpipe using the RM_PIPE_MULTISTAGE_PARALLEL processing mode may produce unexpected results, including an application crash. As of the time of this writing (January 2000), only RM_HARDWARE is supported for 3D RMimage objects. @dend * ---------------------------------------------------- */ RMenum rmImageResize (const RMimage *src, RMimage *dst, RMenum hardwareEnum, RMpipe *hwPipe) { /* * hack. for now, we figure out if the images are 2D or 3D, and * call the appropriate resize routine. */ int src_ndim, dst_ndim, destW, destH, destD; RMenum rstat; /* * validity checking: * 1. src & dst are not NULL. * 2. image formats for both are the same * 3. image ndim for both are the same */ if ((RM_ASSERT(src, "rmImageResize() error: input src RMimage is NULL.") == RM_WHACKED) || (RM_ASSERT(dst, "rmImageResize() error: input dst RMimage is NULL.") == RM_WHACKED)) return(RM_WHACKED); /* we want better validity checking on hwPipe than this. */ if ((hardwareEnum == RM_HARDWARE) && (hwPipe == NULL)) { rmError("rmImageResize() error: a valid RMpipe object must be provided when using RM_HARDWARE as the image resize method."); return(RM_WHACKED); } rmImageGetImageSize(src, &src_ndim, NULL, NULL, NULL, NULL, NULL); rmImageGetImageSize(dst, &dst_ndim, &destW, &destH, &destD, NULL, NULL); if (src_ndim != dst_ndim) { rmError("rmImageResize() error: ndim for src and dst images are not equal."); return(RM_WHACKED); } /* for RM_HARDWARE, check if size of dst image is less than or equal to the size of the display window. */ if (hardwareEnum == RM_HARDWARE) { int winW, winH; rmPipeGetWindowSize(hwPipe, &winW, &winH); if ((winW < destW) || (winH < destH)) { rmError(" rmImageResize() error: when using RM_HARDWARE image resize methods, the destination image dimensions must be less than or equal to the size of the display window."); return(RM_WHACKED); } } #if 0 if (private_rmImageGetFormat(src) != private_rmImageGetFormat(dst)) { rmError("rmImageResize() error: src and dst images are not the same image format."); return(RM_WHACKED); } #endif if (private_rmImageGetNDims(src) == 2) rstat = private_rmImage2DResize(src, dst, hardwareEnum, hwPipe); else if (private_rmImageGetNDims(src) == 3) rstat = private_rmImage3DResize(src, dst, hardwareEnum, hwPipe); else rstat = RM_WHACKED; /* bogus image dimensions */ return(rstat); } /* * ---------------------------------------------------- * @Name rmImageSetScale @pstart RMenum rmImageSetScale (RMimage *toModify, float newScale) @pend @astart RMimage *toModify - a handle to an RMimage object to modify. float newScale - a floating point value that represents the new pixel scale that will be applied to the RMimage object. @aend @dstart Use this routine to set the pixel scale parameter of an RMimage object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. Pixel scale and bias are used in the OpenGL pixel pipeline to modify pixel values as they move from the application into the framebuffer. Inside OpenGL, application pixel components are first converted to the range [0..1] using an algorithm that is a function of the source pixel type (byte, short, etc.). Next, pixels are converted from the source pixel format (luminance-alpha, RGB, etc.) into RGBA. Next, each pixel component is multiplied by the "scale" factor, then "bias" is added. The scale value is simply a linear multiplier that is applied to each pixel value. Note that scale and bias parameters operate on pixel data that has been converted to the range [0..1] according to a type-specific algorithm. Source pixels that are in floating point are not converted, but are clamped to the range [0..1]. Clamping occurs late in the pixel pipeline, only after scale, bias and color lookup have been performed. @dend * ---------------------------------------------------- */ RMenum rmImageSetScale (RMimage *toModify, float newScale) { if (RM_ASSERT(toModify, "rmImageSetScale error: the input RMimage is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->scale = newScale; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmImageGetScale @pstart RMenum rmImageGetScale (const RMimage *toQuery, float *returnScale) @pend @astart const RMimage *toQuery - a handle to an RMimage object to query (input). float *returnScale - a handle to a float that will be set to contain the RMimage object's pixel scale attribute (modified). @aend @dstart Use this routine to obtain the pixel scale parameter of an RMimage object. Returns RM_CHILL upon success, and copies the RMimage object's pixel scale attribute into the caller-supplied memory. Returns RM_WHACKED upon failure. Pixel scale and bias are used in the OpenGL pixel pipeline to modify pixel values as they move from the application into the framebuffer. Inside OpenGL, application pixel components are first converted to the range [0..1] using an algorithm that is a function of the source pixel type (byte, short, etc.). Next, pixels are converted from the source pixel format (luminance-alpha, RGB, etc.) into RGBA. Next, each pixel component is multiplied by the "scale" factor, then "bias" is added. The scale value is simply a linear multiplier that is applied to each pixel value. Note that scale and bias parameters operate on pixel data that has been converted to the range [0..1] according to a type-specific algorithm. Source pixels that are in floating point are not converted, but are clamped to the range [0..1]. Clamping occurs late in the pixel pipeline, only after scale, bias and color lookup have been performed. @dend * ---------------------------------------------------- */ RMenum rmImageGetScale (const RMimage *toQuery, float *returnScale) { if ((RM_ASSERT(toQuery, "rmImageGetScale error: the input RMimage is NULL") == RM_WHACKED) || (RM_ASSERT(returnScale, "rmImageGetScale error: the returnScale float * is NULL") == RM_WHACKED)) return(RM_WHACKED); *returnScale = toQuery->scale; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmImageSetBias @pstart RMenum rmImageSetBias (RMimage *toModify, float newBias) @pend @astart RMimage *toModify - a handle to an RMimage object (modified). float newBias - a floating point value that represents the new pixel bias that will be applied to the RMimage object (input). @aend @dstart Use this routine to set the pixel bias parameter of an RMimage object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. Pixel scale and bias are used in the OpenGL pixel pipeline to modify pixel values as they move from the application into the framebuffer. Inside OpenGL, application pixel components are first converted to the range [0..1] using an algorithm that is a function of the source pixel type (byte, short, etc.). Next, pixels are converted from the source pixel format (luminance-alpha, RGB, etc.) into RGBA. Next, each pixel component is multiplied by the "scale" factor, then "bias" is added. The bias value is simply an offset that is added to each pixel value. Note that scale and bias parameters operate on pixel data that has been converted to the range [0..1] according to a type-specific algorithm. Source pixels that are in floating point are not converted, but are clamped to the range [0..1]. Clamping occurs late in the pixel pipeline, only after scale, bias and color lookup have been performed. @dend * ---------------------------------------------------- */ RMenum rmImageSetBias (RMimage *toModify, float newBias) { if (RM_ASSERT(toModify, "rmImageSetBias error: the input RMimage is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->bias = newBias; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmImageGetBias @pstart RMenum rmImageGetBias (const RMimage *toQuery, float *returnBias) @pend @astart const RMimage *toQuery - a handle to an RMimage object to query (input). float *returnBias - a handle to a float that will be set to contain the RMimage object's bias attribute (modified). @aend @dstart Use this routine to obtain the pixel bias parameter of an RMimage object. Returns RM_CHILL upon success, and copies the RMimage object's bias attribute into the caller-supplied memory. Returns RM_WHACKED upon failure. Pixel scale and bias are used in the OpenGL pixel pipeline to modify pixel values as they move from the application into the framebuffer. Inside OpenGL, application pixel components are first converted to the range [0..1] using an algorithm that is a function of the source pixel type (byte, short, etc.). Next, pixels are converted from the source pixel format (luminance-alpha, RGB, etc.) into RGBA. Next, each pixel component is multiplied by the "scale" factor, then "bias" is added. The bias value is simply an offset that is added to each pixel value. Note that scale and bias parameters operate on pixel data that has been converted to the range [0..1] according to a type-specific algorithm. Source pixels that are in floating point are not converted, but are clamped to the range [0..1]. Clamping occurs late in the pixel pipeline, only after scale, bias and color lookup have been performed. @dend * ---------------------------------------------------- */ RMenum rmImageGetBias (const RMimage *toQuery, float *returnBias) { if ((RM_ASSERT(toQuery, "rmImageGetBias error: the input RMimage is NULL") == RM_WHACKED) || (RM_ASSERT(returnBias, "rmImageGetBias error: the returnBias float * is NULL") == RM_WHACKED)) return(RM_WHACKED); *returnBias = toQuery->bias; return(RM_CHILL); } /* PRIVATE */ int private_rmImageNumComponentBytes (RMenum type_enum) { int rval = 0; switch(type_enum) { case RM_UNSIGNED_BYTE: rval = sizeof(unsigned char); break; case RM_FLOAT: rval = sizeof(float); break; case RM_UNSIGNED_SHORT: case RM_SHORT: rval = sizeof(short); break; default: rmError("private_rmImageNumComponentBytes() error: invalid image type enumerator."); break; } return(rval); } /* PRIVATE */ GLenum private_rmImageGetOGLFormat (const RMimage *img) { int glformat; switch(private_rmImageGetFormat(img)) { case RM_IMAGE_ALPHA: glformat = GL_ALPHA; break; case RM_IMAGE_LUMINANCE_ALPHA: glformat = GL_LUMINANCE_ALPHA; break; case RM_IMAGE_LUMINANCE: glformat = GL_LUMINANCE; break; #if 0 /* deprecated */ case RM_IMAGE_INDEXED_RGB: #endif case RM_IMAGE_RGB: glformat = GL_RGB; break; #if 0 /* deprecated */ case RM_IMAGE_INDEXED_RGBA: #endif case RM_IMAGE_RGBA: glformat = GL_RGBA; break; case RM_IMAGE_DEPTH: glformat = GL_DEPTH_COMPONENT; break; default: rmError(" rmImageGetOGLFormat() error: image format not appropriately handled. "); glformat = GL_RGB; /* ???? */ break; } return(glformat); } /* PRIVATE */ GLenum private_rmImageGetOGLType(const RMimage *img) { int glformat; switch(private_rmImageGetType(img)) { case RM_UNSIGNED_BYTE: glformat = GL_UNSIGNED_BYTE; break; case RM_FLOAT: glformat = GL_FLOAT; break; case RM_SHORT: glformat = GL_SHORT; break; case RM_UNSIGNED_SHORT: glformat = GL_UNSIGNED_SHORT; break; default: rmError(" rmImageGetOGLType() error: image format not appropriately handled. "); glformat = GL_UNSIGNED_BYTE; /* ???? */ break; } return(glformat); } /* PRIVATE */ int private_rmImageGetNumElements (RMenum image_format_enum) { int rstat; /* given an RM image format enumerator, return the number of elements that make up the image */ switch(image_format_enum) { #if 0 /* deprecated */ case RM_IMAGE_INDEXED_RGB: case RM_IMAGE_INDEXED_RGBA: #endif case RM_IMAGE_LUMINANCE: case RM_IMAGE_ALPHA: case RM_IMAGE_DEPTH: rstat = 1; break; case RM_IMAGE_RGB: rstat = 3; break; case RM_IMAGE_RGBA: rstat = 4; break; case RM_IMAGE_LUMINANCE_ALPHA: rstat = 2; break; default: rstat = RM_WHACKED; break; } return(rstat); } /* PRIVATE */ RMenum private_rmImage2DMirrorVertical (RMimage *r) { unsigned char *tmp, *s, *d; int w, h, i, n, elements; RMenum f; if (RM_ASSERT(r, "rmImage2DMirrorVert() error: input RMimage2D is NULL.") == RM_WHACKED) return(RM_WHACKED); w = private_rmImageGetWidth(r); h = private_rmImageGetHeight(r); f = private_rmImageGetFormat(r); elements = private_rmImageGetElements(r); n = private_rmImageGetBytesPerScanline(r); tmp = (unsigned char *)malloc(sizeof(unsigned char)*n); s = rmImageGetPixelData(r); if (RM_ASSERT(s, "private_rmImage2DMirrorVertical() error: the pixel data pointer for the input RMimage is NULL!") == RM_WHACKED) return(RM_WHACKED); d = s + (n * (h - 1)); for (i = 0; i< (h >> 1); i++, s += n, d -= n) { memcpy((void *)tmp, (void *)d, (sizeof(unsigned char) * n)); memcpy((void *)d, (void *)s, (sizeof(unsigned char) * n)); memcpy((void *)s, (void *)tmp, (sizeof(unsigned char) * n)); } free((void *)tmp); return(RM_CHILL); } /* private */ static void private_rmMirrorHorizontalPixels(unsigned char *b, int w, int bytesPerPixel, unsigned char *b2) { int i; int s = 0; int d = (w-1) * bytesPerPixel; for (i=0;iscale; bias = src->bias; /* is the image a depth image? if so, apply scale and bias only to depth, otherwise apply to RGBA channels */ if (private_rmImageGetFormat(src) == RM_IMAGE_DEPTH) { glPixelTransferf(GL_DEPTH_BIAS, bias); glPixelTransferf(GL_DEPTH_SCALE, scale); } else { glPixelTransferf(GL_RED_BIAS, bias); glPixelTransferf(GL_GREEN_BIAS, bias); glPixelTransferf(GL_BLUE_BIAS, bias); glPixelTransferf(GL_ALPHA_BIAS, bias); glPixelTransferf(GL_RED_SCALE, scale); glPixelTransferf(GL_GREEN_SCALE, scale); glPixelTransferf(GL_BLUE_SCALE, scale); glPixelTransferf(GL_ALPHA_SCALE, scale); } return(RM_CHILL); } /* PRIVATE */ RMenum private_rmSetLuminancePixelScale(void) { float bias = 0.0; float rscale = 0.3, gscale = 0.6, bscale = 0.1, ascale = 0.0; glPixelTransferf(GL_RED_BIAS, bias); glPixelTransferf(GL_GREEN_BIAS, bias); glPixelTransferf(GL_BLUE_BIAS, bias); glPixelTransferf(GL_ALPHA_BIAS, bias); glPixelTransferf(GL_RED_SCALE, rscale); glPixelTransferf(GL_GREEN_SCALE, gscale); glPixelTransferf(GL_BLUE_SCALE, bscale); glPixelTransferf(GL_ALPHA_SCALE, ascale); return(RM_CHILL); } /* PRIVATE */ RMenum private_rmUnsetLuminancePixelScale(void) { float bias = 0.0; float rscale = 1.0, gscale = 1.0, bscale = 1.0, ascale = 1.0; glPixelTransferf(GL_RED_BIAS, bias); glPixelTransferf(GL_GREEN_BIAS, bias); glPixelTransferf(GL_BLUE_BIAS, bias); glPixelTransferf(GL_ALPHA_BIAS, bias); glPixelTransferf(GL_RED_SCALE, rscale); glPixelTransferf(GL_GREEN_SCALE, gscale); glPixelTransferf(GL_BLUE_SCALE, bscale); glPixelTransferf(GL_ALPHA_SCALE, ascale); return(RM_CHILL); } /* PRIVATE */ RMenum private_rmImageUnsetGLScaleAndBias(const RMimage *src) { if (private_rmImageGetFormat(src) == RM_IMAGE_DEPTH) { glPixelTransferf(GL_DEPTH_BIAS, 0.0F); glPixelTransferf(GL_DEPTH_SCALE, 1.0F); } else { glPixelTransferf(GL_RED_SCALE, 1.F); glPixelTransferf(GL_GREEN_SCALE, 1.F); glPixelTransferf(GL_BLUE_SCALE, 1.F); glPixelTransferf(GL_ALPHA_SCALE, 1.F); glPixelTransferf(GL_RED_BIAS, 0.F); glPixelTransferf(GL_GREEN_BIAS, 0.F); glPixelTransferf(GL_BLUE_BIAS, 0.F); glPixelTransferf(GL_ALPHA_BIAS, 0.F); } return(RM_CHILL); } /* PRIVATE */ RMenum private_glReadPixels (int x, int y, int width, int height, GLenum format, GLenum type, GLvoid *pixels) { if (format == GL_LUMINANCE) private_rmSetLuminancePixelScale(); glReadPixels(x, y, width, height, format, type, pixels); if (format == GL_LUMINANCE) private_rmUnsetLuminancePixelScale(); return(RM_CHILL); } /* PRIVATE */ RMenum private_glDrawPixels (GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixeldata, const RMimage *src) { int doScaleBias = 0; int doColormap = 0; if (src != NULL) { /* set scale & bias */ if ((src->bias != 0.F) || (src->scale != 1.0F)) { doScaleBias = 1; private_rmImageSetGLScaleAndBias(src); } /* set pixel transfer */ if (src->vismap != NULL) { doColormap = 1; private_rmSetPixelTransferMode(src->vismap); } } #if 0 { GLboolean b; GLfloat f[4]; glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &b); fprintf(stderr," current raster position is %s valid \n", (b == GL_TRUE) ? "definitely" : "not"); glGetFloatv(GL_CURRENT_RASTER_POSITION, f); if (b == GL_TRUE) fprintf(stderr, " rasterpos: %g %g %g \n", f[0], f[1], f[2]); } #endif /* draw pixels */ glDrawPixels(width, height, format, type, pixeldata); /* unset pixel transfer */ if (doScaleBias == 1) private_rmImageUnsetGLScaleAndBias(src); /* unset scale & bias */ if (doColormap == 1) private_rmUnsetPixelTransferMode(); return(RM_CHILL); } /* EOF */