Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
OpenGL_Image_Utilities.c
/* |
* OpenGL_Image_Utilities.c |
* OpenGL Image |
* |
* Created by ggs on Fri May 11 2001. |
Copyright: Copyright © 2001 Apple Computer, Inc., All Rights Reserved |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* |
*/ |
#ifdef __APPLE_CC__ // project builder |
#include <Carbon/Carbon.h> // standard carbon |
#include <OpenGL/gl.h> // for OpenGL API |
#include <OpenGL/glext.h> // for OpenGL extension support |
#else // CodeWarrior |
#include <FixMath.h> // for X2Fix |
#include <fp.h> // for trig functions |
#include <DriverServices.h> |
#include <gl.h> // for OpenGL API |
#include <glext.h> // for OpenGL extension support |
#include <stdio.h> // standard input/output (sprintf) |
#include <string.h> // for string handling (strstr) |
#endif |
#include "aglString.h" // opengl string handling |
#include "Carbon_SetupGL.h" // standard setup code (SetupGL) |
#include "Carbon_Error_Handler.h" // standard SetupGL error handling |
#include "OpenGL_Image.h" // our header |
// drag globals |
Point gptDragStart = {0, 0}; // starting point at mouse down for drag |
WindowRef gDragWindow = NULL; // the window in which the mouse is dragging |
short gDragType = 0; // either kDrag or kRotation to indicate what type of drag the user is doing |
// ================================== |
static void DrawGLTexelGrid (float textureWidth, float textureHeight, float imageWidth, float imageHeight, float zoom); // in pixels |
static long GetNextTextureSize (long textureDimension, long maxTextureSize, Boolean textureRectangle); |
static long GetTextureNumFromTextureDim (long textureDimension, long maxTextureSize, Boolean texturesOverlap, Boolean textureRectangle); |
static void DrawGLImageTile (unsigned long drawType, float imageWidth, float imageHeight, float zoom, float textureWidth, float textureHeight, |
float offsetX, float offsetY, float endX, float endY, Boolean texturesOverlap, Boolean textureRectangle); |
pascal void IdleTimer (EventLoopTimerRef inTimer, void* userData); |
EventLoopTimerUPP GetTimerUPP (void); |
// ================================== |
// private |
// draws grid lines in current color between each texel in the texture as it corresponds to orginal image |
// i.e., draws grid lines based on texels but constrains output to image dimensions |
static void DrawGLTexelGrid (float textureWidth, float textureHeight, float imageWidth, float imageHeight, float zoom) // in pixels |
{ |
long i; // iterator |
float perpenCoord, coord, coordStep; // perpendicular coordinate, dawing (iteratoring) coordinate, coordiante step amount per line |
glBegin (GL_LINES); // draw using lines |
// vertical lines |
perpenCoord = 0.5f * imageHeight * zoom; // 1/2 height of image in world space |
coord = -0.5f * imageWidth * zoom; // starting scaled coordinate for half of image width (world space) |
coordStep = imageWidth / textureWidth * zoom; // space between each line (maps texture size to image size) |
for (i = 0; i <= textureWidth; i++) // ith column |
{ |
glVertex3f (coord, -perpenCoord, 0.0f); // draw from current column, top of image to... |
glVertex3f (coord, perpenCoord, 0.0f); // current column, bottom of image |
coord += coordStep; // step to next column |
} |
// horizontal lines |
perpenCoord = 0.5f * imageWidth * zoom; // 1/2 width of image in world space |
coord = -0.5f * imageHeight * zoom; // scaled coordinate for half of image height (actual drawing coords) |
coordStep = imageHeight / textureHeight * zoom; // space between each line (maps texture size to image size) |
for (i = 0; i <= textureHeight; i++) // ith row |
{ |
glVertex3f (-perpenCoord, coord, 0.0f); // draw from current row, left edge of image to... |
glVertex3f (perpenCoord, coord, 0.0f);// current row, right edge of image |
coord += coordStep; // step to next row |
} |
glEnd(); // end our set of lines |
} |
// --------------------------------- |
// returns the largest power of 2 texture <= textureDimension and <= gMaxTextureSize |
// or in the case of texture rectangle returns the next texture size (can be non-power of two) |
static long GetNextTextureSize (long textureDimension, long maxTextureSize, Boolean textureRectangle) |
{ |
long targetTextureSize = maxTextureSize; // start at max texture size |
if (textureRectangle) |
{ |
if (textureDimension >= targetTextureSize) // the texture dimension is greater than the target texture size (i.e., it fits) |
return targetTextureSize; // return corresponding texture size |
else |
return textureDimension; // jusr return the dimension |
} |
else |
{ |
do // while we have txture sizes check for texture value being equal or greater |
{ |
if (textureDimension >= targetTextureSize) // the texture dimension is greater than the target texture size (i.e., it fits) |
return targetTextureSize; // return corresponding texture size |
} |
while (targetTextureSize >>= 1); // step down to next texture size smaller |
} |
return 0; // no textures fit so return zero |
} |
// --------------------------------- |
// returns the nuber of textures need to represent a size of textureDimension given |
// requirement for power of 2 textures and gMaxTextureSize as the maximum texture size |
// for the overlap case each texture effectively covers two less pixels so must iterate through using whole statement |
static long GetTextureNumFromTextureDim (long textureDimension, long maxTextureSize, Boolean texturesOverlap, Boolean textureRectangle) |
{ |
// start at max texture size |
// loop through each texture size, removing textures in turn which are less than the remaining texture dimension |
// each texture has 2 pixels of overlap (one on each side) thus effective texture removed is 2 less than texture size |
long i = 0; // initially no textures |
long bitValue = maxTextureSize; // start at max texture size |
long texOverlapx2 = texturesOverlap ? 2 : 0; |
textureDimension -= texOverlapx2; // ignore texture border since we are using effective texure size (by subtracting 2 from the initial size) |
if (textureRectangle) |
{ |
// count number of full textures |
while (textureDimension > (bitValue - texOverlapx2)) // while our texture dimension is greater than effective texture size (i.e., minus the border) |
{ |
i++; // count a texture |
textureDimension -= bitValue - texOverlapx2; // remove effective texture size |
} |
// add one partial texture |
i++; |
} |
else |
{ |
do |
{ |
while (textureDimension >= (bitValue - texOverlapx2)) // while our texture dimension is greater than effective texture size (i.e., minus the border) |
{ |
i++; // count a texture |
textureDimension -= bitValue - texOverlapx2; // remove effective texture size |
} |
} |
while ((bitValue >>= 1) > texOverlapx2); // step down to next texture while we are greater than two (less than 4 can't be used due to 2 pixel overlap) |
if (textureDimension > 0x0) // if any textureDimension is left there is an error, because we can't texture these small segments and in anycase should not have image pixels left |
ReportErrorNum ("GetTextureNumFromTextureDim error: Texture to small to draw, should not ever get here, texture size remaining:", textureDimension); |
} |
return i; // return textures counted |
} |
// --------------------------------- |
// draws a single texture piece of the image |
// offset is the start point in the image and end is the end point |
// all is relative to orginal image size so center around center of image and zoom as required |
static void DrawGLImageTile (unsigned long drawType, float imageWidth, float imageHeight, float zoom, float textureWidth, float textureHeight, |
float offsetX, float offsetY, float endX, float endY, Boolean texturesOverlap, Boolean textureRectangle) |
{ |
float startXDraw = (offsetX - imageWidth * 0.5f) * zoom; // left edge of poly: offset is in image local coordinates convert to world coordinates |
float endXDraw = (endX - imageWidth * 0.5f) * zoom; // right edge of poly: offset is in image local coordinates convert to world coordinates |
float startYDraw = (offsetY - imageHeight * 0.5f) * zoom; // top edge of poly: offset is in image local coordinates convert to world coordinates |
float endYDraw = (endY - imageHeight * 0.5f) * zoom; // bottom edge of poly: offset is in image local coordinates convert to world coordinates |
float texOverlap = texturesOverlap ? 1.0f : 0.0f; // size of texture overlap, switch based on whether we are using overlap or not |
float startXTexCoord = texOverlap / (textureWidth + 2.0f * texOverlap); // texture right edge coordinate (stepped in one pixel for border if required) |
float endXTexCoord = 1.0f - startXTexCoord; // texture left edge coordinate (stepped in one pixel for border if required) |
float startYTexCoord = texOverlap / (textureHeight + 2.0f * texOverlap); // texture top edge coordinate (stepped in one pixel for border if required) |
float endYTexCoord = 1.0f - startYTexCoord; // texture bottom edge coordinate (stepped in one pixel for border if required) |
if (textureRectangle) |
{ |
startXTexCoord = texOverlap; // texture right edge coordinate (stepped in one pixel for border if required) |
endXTexCoord = textureWidth + texOverlap; // texture left edge coordinate (stepped in one pixel for border if required) |
startYTexCoord = texOverlap; // texture top edge coordinate (stepped in one pixel for border if required) |
endYTexCoord = textureHeight + texOverlap; // texture bottom edge coordinate (stepped in one pixel for border if required) |
} |
if (endX > (imageWidth + 0.5)) // handle odd image sizes, (+0.5 is to ensure there is no fp resolution problem in comparing two fp numbers) |
{ |
endXDraw = (imageWidth * 0.5f) * zoom; // end should never be past end of image, so set it there |
if (textureRectangle) |
endXTexCoord -= 1.0f; |
else |
endXTexCoord = 1.0f - 2.0f * startXTexCoord; // for the last texture in odd size images there are two texels of padding so step in 2 |
} |
if (endY > (imageHeight + 0.5f)) // handle odd image sizes, (+0.5 is to ensure there is no fp resolution problem in comparing two fp numbers) |
{ |
endYDraw = (imageHeight * 0.5f) * zoom; // end should never be past end of image, so set it there |
if (textureRectangle) |
endYTexCoord -= 1.0f; |
else |
endYTexCoord = 1.0f - 2.0f * startYTexCoord; // for the last texture in odd size images there are two texels of padding so step in 2 |
} |
glBegin (drawType); // draw either tri strips of line strips (so this will drw either two tris or 3 lines) |
glTexCoord2f (startXTexCoord, startYTexCoord); // draw upper left in world coordinates |
glVertex3d (startXDraw, startYDraw, 0.0); |
glTexCoord2f (endXTexCoord, startYTexCoord); // draw lower left in world coordinates |
glVertex3d (endXDraw, startYDraw, 0.0); |
glTexCoord2f (startXTexCoord, endYTexCoord); // draw upper right in world coordinates |
glVertex3d (startXDraw, endYDraw, 0.0); |
glTexCoord2f (endXTexCoord, endYTexCoord); // draw lower right in world coordinates |
glVertex3d (endXDraw, endYDraw, 0.0); |
glEnd(); |
// finish strips |
if (drawType == GL_LINE_STRIP) // draw top and bottom lines which were not draw with above |
{ |
glBegin (GL_LINES); |
glVertex3d(startXDraw, endYDraw, 0.0); // top edge |
glVertex3d(startXDraw, startYDraw, 0.0); |
glVertex3d(endXDraw, startYDraw, 0.0); // bottom edge |
glVertex3d(endXDraw, endYDraw, 0.0); |
glEnd(); |
} |
} |
// --------------------------------- |
// idle timer to window auto rotation updates |
pascal void IdleTimer (EventLoopTimerRef inTimer, void* userData) |
{ |
#pragma unused (inTimer) |
pRecImage pWindowInfo = NULL; |
WindowRef window = (WindowRef) userData; |
if (window) |
pWindowInfo = (pRecImage) GetWRefCon (window); |
if (pWindowInfo) |
DrawGL (window); |
} |
// --------------------------------- |
// builds timer UPP |
EventLoopTimerUPP GetTimerUPP (void) |
{ |
static EventLoopTimerUPP sTimerUPP = NULL; |
if (sTimerUPP == NULL) |
sTimerUPP = NewEventLoopTimerUPP (IdleTimer); |
return sTimerUPP; |
} |
#pragma mark - |
// ================================== |
// public |
// disposes OpenGL context, and associated texture list |
OSStatus DisposeGLForWindow (WindowRef window) |
{ |
if (window) |
{ |
pRecImage pWindowInfo = (pRecImage) GetWRefCon (window); // get gl data stored in refcon |
SetWRefCon (window, NULL); // ensure the refcon is not used again |
if (NULL == pWindowInfo) // if this is non-existant |
return paramErr; // then drop out |
if (NULL != pWindowInfo->aglContext) |
{ |
aglSetCurrentContext (pWindowInfo->aglContext); // ensaure the context we are working with is set to current |
aglUpdateContext (pWindowInfo->aglContext); // ensaure the context we are working with is set to current |
glFinish (); // ensure all gl commands are complete |
glDeleteTextures (pWindowInfo->textureX * pWindowInfo->textureY, pWindowInfo->pTextureName); // delete the complete set of textures used for the window |
DestroyGLFromWindow (&pWindowInfo->aglContext, &pWindowInfo->glInfo); // preoperly destroy GL context and any associated structures |
pWindowInfo->aglContext = NULL; // ensure we don't use invlad context |
} |
if (NULL != pWindowInfo->pTextureName) |
{ |
DisposePtr ((Ptr) pWindowInfo->pTextureName); // dispose of the allocate4d texture name storage |
pWindowInfo->pTextureName = NULL; // ensure we do not use it again |
} |
if (pWindowInfo->pImageBuffer) // MUST preserve the buffer if texturing from client memory |
{ |
DisposePtr ((Ptr) pWindowInfo->pImageBuffer); // or image buffer |
pWindowInfo->pImageBuffer = NULL; |
} |
DisposePtr ((Ptr) pWindowInfo); |
return noErr; // we are good to go |
} |
else |
return paramErr; // NULL window ref passed in |
} |
// --------------------------------- |
// builds the GL context and associated state for the window |
// loads image into a texture or textures |
// disposes of GWorld and image buffer when finished loading textures |
OSStatus BuildGLForWindow (WindowRef window) |
{ |
GrafPtr portSave = NULL; // port which is set on entrance to this routine |
pRecImage pWindowInfo = (pRecImage) GetWRefCon (window); // the info structure for the window stored in the refcon |
short i; // iterator |
short fNum; // font number |
GLenum textureTarget = GL_TEXTURE_2D; |
if (!pWindowInfo->aglContext) // if we get here and do not have a context built, build one |
{ |
GetPort (&portSave); // save current port |
SetPort ((GrafPtr) GetWindowPort (window)); // set port to the current window |
// set parameters for Carbon SetupGL |
pWindowInfo->glInfo.fAcceleratedMust = false; // must renderer be accelerated? |
pWindowInfo->glInfo.VRAM = 0 * 1048576; // minimum VRAM (if not zero this is always required) |
pWindowInfo->glInfo.textureRAM = 0 * 1048576; // minimum texture RAM (if not zero this is always required) |
if (!CheckMacOSX ()) |
pWindowInfo->glInfo.fDraggable = false; // is this a draggable window |
else |
pWindowInfo->glInfo.fDraggable = true; // is this a draggable window |
pWindowInfo->glInfo.fmt = 0; // output pixel format |
i = 0; // first attribute in array |
pWindowInfo->glInfo.aglAttributes [i++] = AGL_RGBA; // RGB + Alpha pixels |
pWindowInfo->glInfo.aglAttributes [i++] = AGL_DOUBLEBUFFER; // doble buffered context |
pWindowInfo->glInfo.aglAttributes [i++] = AGL_ACCELERATED; // require hardware acceleration |
pWindowInfo->glInfo.aglAttributes [i++] = AGL_NO_RECOVERY; // Mac OS X 10.0.4 has problems with the GL (disregards UNPACK_ROW_LENGTH) resulting from using no recovery |
// normally we would use no recovery to ensure the minimum pixel size textures are stored by GL. |
pWindowInfo->glInfo.aglAttributes [i++] = AGL_NONE; // end parameter list |
BuildGLFromWindow (window, &(pWindowInfo->aglContext), &(pWindowInfo->glInfo), NULL); // build opengl context for our window |
if (!pWindowInfo->aglContext) // if could not create context |
DestroyGLFromWindow (&pWindowInfo->aglContext, &pWindowInfo->glInfo); // ensure context is destroyed correctly |
else // we have a valid context |
{ |
GLint swap = 0; // swap interval (i.e., VBL sync) setting 1 = sync, 0 = no sync |
Rect rectPort; // window port rectangle |
long width = pWindowInfo->imageWidth, height = pWindowInfo->imageHeight; // image width and height |
GDHandle device; // GDevice to find the constrain the window to |
Rect deviceRect, availRect, rect; // rect of device which window is on (mostly, area wise at least). avialable area for window (minus dock and menu if req), working rect |
GetWindowGreatestAreaDevice (window, kWindowContentRgn, &device, &deviceRect); // find device the window is mostly on |
GetAvailableWindowPositioningBounds (device, &availRect); // get the geretest available area for te windoew (mminus doc and menu if applicable) |
if (width > (availRect.right - availRect.left)) // adjust window width if it is greater than available area (orginally set to image width, see above) |
width = (availRect.right - availRect.left); |
if (height > (availRect.bottom - availRect.top)) // adjust window height if it is greater than available area (orginally set to image width, see above) |
height = (availRect.bottom - availRect.top); |
SizeWindow (window, (short) width, (short) height, true); // size the window to new width and height |
ConstrainWindowToScreen(window, kWindowStructureRgn, kWindowConstrainMayResize, NULL, &rect); // ensure window structure region is on the screen |
GetWindowPortBounds (window, &rectPort); // get port rect for viewport reset |
aglSetCurrentContext (pWindowInfo->aglContext); // set our GL context to this one |
aglUpdateContext (pWindowInfo->aglContext); // update the context to account for the resize |
InvalWindowRect (window, &rectPort); // inval the entire window to ensure we get a redraw |
glViewport (0, 0, rectPort.right - rectPort.left, rectPort.bottom - rectPort.top); // reset viewport to entier window area |
aglSetInteger (pWindowInfo->aglContext, AGL_SWAP_INTERVAL, &swap); // set swap interval to account for vbl syncing or not |
// set correct texture target |
#ifdef GL_TEXTURE_RECTANGLE_EXT // if building on 10.0 or 9 this will be undefined |
if (pWindowInfo->fNPOTTextures) |
textureTarget = GL_TEXTURE_RECTANGLE_EXT; |
#endif |
// Set texture mapping parameters |
glEnable (textureTarget); // enable texturing |
glClearColor(0.1f, 0.1f, 0.2f, 1.0f); // set clear color buffer to dark gray |
glClear (GL_COLOR_BUFFER_BIT); // clear just to color buffer |
aglSwapBuffers (pWindowInfo->aglContext); // swap the cleared buffer to front |
GetFNum ("\pMonaco", &fNum); // get font number for named font |
pWindowInfo->fontList = BuildFontGL (pWindowInfo->aglContext, fNum, normal, 9); // build display list for fonts for this context (see aglString for more info) |
// if we can use texture rectangle |
#ifdef GL_TEXTURE_RECTANGLE_EXT // if building on 10.0 or 9 this will be undefined |
if (pWindowInfo->fNPOTTextures) |
glEnable(GL_TEXTURE_RECTANGLE_EXT); |
#endif |
glPixelStorei (GL_UNPACK_ROW_LENGTH, pWindowInfo->textureWidth); // set image width in groups (pixels), accounts for border this ensures proper image alignment row to row |
// get number of textures x and y |
// extract the number of horiz. textures needed to tile image |
pWindowInfo->textureX = GetTextureNumFromTextureDim (pWindowInfo->textureWidth, pWindowInfo->maxTextureSize, pWindowInfo->fOverlapTextures, pWindowInfo->fNPOTTextures); |
// extract the number of horiz. textures needed to tile image |
pWindowInfo->textureY = GetTextureNumFromTextureDim (pWindowInfo->textureHeight, pWindowInfo->maxTextureSize, pWindowInfo->fOverlapTextures, pWindowInfo->fNPOTTextures); |
pWindowInfo->pTextureName = (GLuint *) NewPtrClear ((long) sizeof (GLuint) * pWindowInfo->textureX * pWindowInfo->textureY); // allocate storage for texture name lists |
glGenTextures (pWindowInfo->textureX * pWindowInfo->textureY, pWindowInfo->pTextureName); // generate textures names need to support tiling |
{ |
long x, y, k = 0, offsetY, offsetX = 0, currWidth, currHeight; // texture iterators, texture name iterator, image offsets for tiling, current texture width and height |
for (x = 0; x < pWindowInfo->textureX; x++) // for all horizontal textures |
{ |
currWidth = GetNextTextureSize (pWindowInfo->textureWidth - offsetX, pWindowInfo->maxTextureSize, pWindowInfo->fNPOTTextures); // use remaining to determine next texture size |
// (basically greatest power of 2 which fits into remaining space) |
offsetY = 0; // reset vertical offest for every column |
for (y = 0; y < pWindowInfo->textureY; y++) // for all vertical textures |
{ |
// buffer pointer is at base + rows * row size + columns |
unsigned char * pBuffer = pWindowInfo->pImageBuffer + |
offsetY * pWindowInfo->textureWidth * (pWindowInfo->imageDepth >> 3) + |
offsetX * (pWindowInfo->imageDepth >> 3); |
currHeight = GetNextTextureSize (pWindowInfo->textureHeight - offsetY, pWindowInfo->maxTextureSize, pWindowInfo->fNPOTTextures); // use remaining to determine next texture size |
glBindTexture (textureTarget, pWindowInfo->pTextureName[k++]); |
if (pWindowInfo->fAGPTexturing) |
glTexParameterf (textureTarget, GL_TEXTURE_PRIORITY, 0.0f); // AGP texturing |
else |
glTexParameterf (textureTarget, GL_TEXTURE_PRIORITY, 1.0f); |
#ifdef GL_UNPACK_CLIENT_STORAGE_APPLE |
if (pWindowInfo->fClientTextures) |
glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, 1); |
else |
glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, 0); |
#endif |
glTexParameteri (textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
glTexParameteri (textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
glTexParameteri (textureTarget, GL_TEXTURE_WRAP_S, gpOpenGLCaps->edgeClampParam); |
glTexParameteri (textureTarget, GL_TEXTURE_WRAP_T, gpOpenGLCaps->edgeClampParam); |
glReportError (); // report any errors so far |
glTexImage2D (textureTarget, 0, GL_RGBA, currWidth, currHeight, 0, |
GL_BGRA_EXT, pWindowInfo->imageDepth == 32 ? GL_UNSIGNED_INT_8_8_8_8_REV : GL_UNSIGNED_SHORT_1_5_5_5_REV, |
pBuffer); // texture with current width and height at pBuffer location in image buffer with image size as GL_UNPACK_ROW_LENGTH |
glReportError (); // report any errors |
offsetY += currHeight - 2 * pWindowInfo->fOverlapTextures; // offset in for the amount of texture used, |
// since we are overlapping the effective texture used is 2 texels less than texture width |
} |
offsetX += currWidth - 2 * pWindowInfo->fOverlapTextures; // offset in for the amount of texture used, |
// since we are overlapping the effective texture used is 2 texels less than texture width |
} |
} |
if (false == pWindowInfo->fClientTextures) // MUST preserve the buffer if texturing from client memory |
{ |
DisposePtr ((Ptr) pWindowInfo->pImageBuffer); // or image buffer |
pWindowInfo->pImageBuffer = NULL; |
} |
} |
SetPort (portSave); //reset port |
} |
return noErr; // we done |
} |
// --------------------------------- |
// Handle updating context for window moves and resizing |
OSStatus ResizeMoveGLWindow (WindowRef window) |
{ |
OSStatus err = noErr; // no errors to start |
Rect rectPort; // new port rect |
if (NULL != window) // if we have a window |
{ |
pRecImage pWindowInfo = (pRecImage) GetWRefCon (window); // get GL info |
if (NULL != pWindowInfo) // if we have gl info for the window |
if (!aglUpdateContext (pWindowInfo->aglContext)) // update the context to ensure gl knows about the move or resize |
aglReportError (); // report any error with update context |
GetWindowPortBounds (window, &rectPort); // find new bounds |
err = InvalWindowRect (window, &rectPort); // inval entire bounds to ensure a redraw (which will be superfluous on moves but not a big deal |
if (noErr != err) |
ReportErrorNum ("ResizeMoveGLWindow error with InvalWindowRect on window: ", err); // should not get erro here, but who knows |
} |
else |
err = paramErr; // bad window |
return err; // return any error |
} |
// --------------------------------- |
// resets tracking of mouse drag when wmouse is released |
void MouseUpGLWindow (void) |
{ |
gptDragStart.v = 0; // reset tracking variables to idle state |
gptDragStart.h = 0; |
gDragWindow = NULL; |
} |
// --------------------------------- |
// starts tracking of mouse down for drag calculatons |
// need to record drag window local position of the mouse and whether we are dragging or rotating |
void MouseDownGLWindow (WindowRef window, Point mousePoint, UInt32 modifiers) |
{ |
GrafPtr portSave; |
if (NULL != window) // check for valid window |
{ |
GetPort (&portSave); // save current port |
SetPort (GetWindowPort (window)); // set window port (for global to local call) |
GlobalToLocal (&mousePoint); //convert mouse coordinates to local coordintes prior to recording |
gptDragStart = mousePoint; // record start point |
gDragWindow = window; // record window we are dragging in |
gDragType = kDrag; // default is to drag |
if (modifiers & cmdKey) // if option key is down |
gDragType = kRotation; // rotate instead of dragging |
else if (modifiers & optionKey) |
gDragType = kZoom; // rotate instead of dragging |
SetPort (portSave); // reset port |
} |
} |
// --------------------------------- |
// main working functioning hamdling live drag and rotation |
// moves center based on mouse delta from last check or rotates image based on nagular chnage of mouse (from image center) |
void DragGLWindow (Point dragPoint) |
{ |
GrafPtr portSave; // port to save |
Rect rectPort; // reactangle for current point |
float fPi = 3.14159265f; // our PI approximation |
if (NULL != gDragWindow) // use window as latch for dragging being live |
{ |
pRecImage pWindowInfo = (pRecImage) GetWRefCon (gDragWindow); // get the gl info from the ref con of the window |
GetPort (&portSave); // save current port |
SetPort (GetWindowPort (gDragWindow)); // set port to our drag window |
GlobalToLocal (&dragPoint); // convert current point to window local (as start point is also window local) |
if (pWindowInfo) // if we have valid gl info for the window |
{ |
GetWindowPortBounds (gDragWindow, &rectPort); // get our bounds rect |
if (gDragType == kDrag) // if we are dragging (as opposed to rotating |
{ |
pWindowInfo->centerX += dragPoint.h - gptDragStart.h; // just offset the center of the window by the delta (start to current) |
pWindowInfo->centerY += dragPoint.v - gptDragStart.v; |
} |
else if (gDragType == kRotation) // if we are rotating |
{ |
float imageCenterX = rectPort.left + (rectPort.right - rectPort.left) / 2 + pWindowInfo->centerX; // find the image center relative to port |
float imageCenterY = rectPort.top + (rectPort.bottom - rectPort.top) / 2 + pWindowInfo->centerY; |
float deltaX, deltaY; // delta from drag point to image center |
float angle1, angle2; // angular representation of start and current angles off vertical for mouse points (relative to image center |
deltaX = imageCenterX - dragPoint.h; // find delta vector current drag point to image center |
deltaY = imageCenterY - dragPoint.v; |
if (deltaX == 0.0) // if vector is vertical |
if (deltaY < 0.0) angle1 = 0.0f; // and Y is negative, the angle is 0 (i.e., pointing up) |
else angle1 = fPi; // else Y must be zero or poistive so vector in PI radians (i.e., pointing down) |
else // if X is not 0, thus no disparate case |
{ |
angle1 = (float) atan (deltaY / deltaX); // the angle (0 to PI / 2) is the arctangent of rise over run (Y / X) |
if (deltaX < 0.0) angle1 += 1.5 * fPi; // if X is negative then add 3 / 2 PI (270¡) to the angle from above |
else angle1 += 0.5 * fPi; // otherwise rotate by 1 / 2 PI (90¡) to angle |
} |
deltaX = imageCenterX - gptDragStart.h; // find delta vector drag start point to image center |
deltaY = imageCenterY - gptDragStart.v; |
if (deltaX == 0.0) // if vector is vertical |
if (deltaY < 0.0) angle2 = 0.0f; // and Y is negative, the angle is 0 (i.e., pointing up) |
else angle2 = fPi; // else Y must be zero or poistive so vector in PI radians (i.e., pointing down) |
else |
{ |
angle2 = (float) atan (deltaY / deltaX); // the angle (0 to PI / 2) is the arctangent of rise over run (Y / X) |
if (deltaX < 0.0) angle2 += 1.5 * fPi; // if X is negative then add 3 / 2 PI (270¡) to the angle from above |
else angle2 += 0.5 * fPi; // otherwise rotate by 1 / 2 PI (90¡) to angle |
} |
angle2 -= angle1; // find angle delta (the difference between the start angle and the current angle |
if (angle2 > fPi) angle2 -= fPi * 2.0; // correct for shortest path |
if (angle2 < -fPi) angle2 += fPi * 2.0; |
pWindowInfo->rotation -= angle2 * 180 / fPi; // convert to degrees and increment ratation angle |
while (pWindowInfo->rotation >= 360.0) pWindowInfo->rotation -= 360.0; // ensure within -360 to 360 |
while (pWindowInfo->rotation <= -360.0) pWindowInfo->rotation += 360.0; |
} |
else if (gDragType == kZoom) // if we are rotating |
{ |
float zoomDelta = (float)(dragPoint.v - gptDragStart.v) * 0.01f; // just offset the center of the window by the delta (start to current) |
zoomDelta = 1.0f - zoomDelta; |
if (zoomDelta < 0.0001f) // prevetns negative zoom values (which would invert the image) |
zoomDelta = 0.0001f; |
pWindowInfo->zoom *= zoomDelta; |
pWindowInfo->centerX *= zoomDelta; |
pWindowInfo->centerY *= zoomDelta; |
} |
gptDragStart = dragPoint; // reset point to the current point so next pass through will work incrementally |
InvalWindowRect (gDragWindow, &rectPort); // ensure window will be redrawn |
} |
SetPort (portSave); // reset port |
} |
} |
// --------------------------------- |
void StartRotation (WindowRef window) |
{ |
if (window) |
{ |
pRecImage pWindowInfo = (pRecImage) GetWRefCon (window); // get gl data stored in refcon |
if (pWindowInfo) |
{ |
if (pWindowInfo->timer) |
RemoveEventLoopTimer(pWindowInfo->timer); |
pWindowInfo->timer = NULL; |
InstallEventLoopTimer (GetCurrentEventLoop(), 0, pWindowInfo->timerInterval, GetTimerUPP (), (void *) window, &(pWindowInfo->timer)); |
} |
} |
} |
// --------------------------------- |
void StopRotation (WindowRef window) |
{ |
if (window) |
{ |
pRecImage pWindowInfo = (pRecImage) GetWRefCon (window); // get gl data stored in refcon |
if (pWindowInfo) |
{ |
if (pWindowInfo->timer) |
RemoveEventLoopTimer(pWindowInfo->timer); |
pWindowInfo->timer = NULL; |
} |
} |
} |
// --------------------------------- |
// main GL drawing routine, should be valid window passed in (will setupGL if require). Draw image, then grid, then edges, then information |
void DrawGL (WindowRef window) |
{ |
Rect rectPort; // rectangle for port |
pRecImage pWindowInfo; // the gl info for the target window |
long width, height, yRaster = 1; // width and height or the port and the row of the raster position |
char cstr [256]; // string to output to screen |
long effectiveTextureMod = 0; // texture size modification (inset) to account for borders |
long x, y, k = 0, offsetY, offsetX = 0, currTextureWidth, currTextureHeight; |
GLenum textureTarget = GL_TEXTURE_2D; |
if (NULL == window) // if we do not have a window |
return; // drop out |
pWindowInfo = (pRecImage) GetWRefCon (window); // get the gl info for the window |
if (NULL == pWindowInfo) // if this is non-existant |
return; // then drop out |
if (NULL == pWindowInfo->aglContext) // try to buld the context if we don't have one (safety check) |
BuildGLForWindow (window); |
if (NULL == pWindowInfo->aglContext) // if we still don't have one then drop out |
return; |
// adjust timer |
if (pWindowInfo->spinning) |
{ |
AbsoluteTime currTime = UpTime (); |
float frameRate, deltaTime = (float) AbsoluteDeltaToDuration (currTime, pWindowInfo->time); |
if (0 > deltaTime) // if negative microseconds |
deltaTime /= -1000000.0; |
else // else milliseconds |
deltaTime /= 1000.0; |
frameRate = (pWindowInfo->frames + 1) / deltaTime; |
if (frameRate * pWindowInfo->timerInterval > 1.0f) // if timer too slow, increase rate |
{ |
pWindowInfo->timerInterval *= 0.80; // reduce interval |
RemoveEventLoopTimer(pWindowInfo->timer); |
InstallEventLoopTimer (GetCurrentEventLoop(), 0, pWindowInfo->timerInterval, GetTimerUPP (), (void *) window, &(pWindowInfo->timer)); |
} |
// spin |
pWindowInfo->rotation += 1.0; |
while (pWindowInfo->rotation >= 360.0) pWindowInfo->rotation -= 360.0; // ensure within -360 to 360 |
while (pWindowInfo->rotation <= -360.0) pWindowInfo->rotation += 360.0; |
} |
if (pWindowInfo->fOverlapTextures) |
effectiveTextureMod = 2; // if we overlap then we need to inset the textures passed to the drawing code |
// set texture target |
#ifdef GL_TEXTURE_RECTANGLE_EXT |
if (pWindowInfo->fNPOTTextures) |
textureTarget = GL_TEXTURE_RECTANGLE_EXT; |
#endif |
aglSetCurrentContext (pWindowInfo->aglContext); // ensaure the context we are working with is set to current |
aglUpdateContext (pWindowInfo->aglContext); // ensaure the context we are working with is set to current |
GetWindowPortBounds (window, &rectPort); // get the current port (window) bounds |
width = rectPort.right - rectPort.left; // find width |
height = rectPort.bottom - rectPort.top; // and height |
glViewport (0, 0, width, height); // set the viewport to cover entire window |
glMatrixMode (GL_PROJECTION); // set projection matrix |
glLoadIdentity (); // to indetity |
glMatrixMode (GL_MODELVIEW); // set modelview matrix |
glLoadIdentity (); // to identity |
glReportError (); // report any GL errors so far |
// set the model view matrix for an orthographic view scaled to one screen pixel equal image pixel (independent of image zoom) |
glScalef (2.0f / width, -2.0f / height, 1.0f); // scale to port per pixel scale |
glTranslatef (pWindowInfo->centerX, pWindowInfo->centerY, 0.0f); // translate for image movement |
glRotatef (pWindowInfo->rotation, 0.0f, 0.0f, 1.0f); // ratate matrix for image rotation |
glReportError (); // report any GL errors |
glClear (GL_COLOR_BUFFER_BIT); // clear the color buffer before drawing |
// draw image |
glEnable (textureTarget); // enable texturing |
glColor3f (1.0f, 1.0f, 1.0f); // white polygons |
// offset x and y are used to draw the polygon and need to represent the texture effective edges (without borders) |
// so walk the texture size images adjusting for each border |
for (x = 0; x < pWindowInfo->textureX; x++) // for all horizontal textures |
{ |
// use remaining to determine next texture size |
currTextureWidth = GetNextTextureSize (pWindowInfo->textureWidth - offsetX, pWindowInfo->maxTextureSize, pWindowInfo->fNPOTTextures) - effectiveTextureMod; // current effective texture width for drawing |
offsetY = 0; // start at top |
for (y = 0; y < pWindowInfo->textureY; y++) // for a complete column |
{ |
// use remaining to determine next texture size |
currTextureHeight = GetNextTextureSize (pWindowInfo->textureHeight - offsetY, pWindowInfo->maxTextureSize, pWindowInfo->fNPOTTextures) - effectiveTextureMod; // effective texture height for drawing |
glBindTexture(textureTarget, pWindowInfo->pTextureName[k++]); // work through textures in same order as stored, setting each texture name as current in turn |
glReportError (); // report any errors |
DrawGLImageTile (GL_TRIANGLE_STRIP, pWindowInfo->imageWidth, pWindowInfo->imageHeight, pWindowInfo->zoom, |
currTextureWidth, currTextureHeight, // draw this single texture on two tris |
offsetX, offsetY, |
pWindowInfo->fTileTextures ? currTextureWidth + offsetX : pWindowInfo->imageWidth, |
pWindowInfo->fTileTextures ? currTextureHeight + offsetY : pWindowInfo->imageHeight, |
pWindowInfo->fOverlapTextures, pWindowInfo->fNPOTTextures); |
glReportError (); // report any errors |
offsetY += currTextureHeight; // offset drawing position for next texture vertically |
} |
offsetX += currTextureWidth; // offset drawing position for next texture horizontally |
} |
glReportError (); // report any errors |
glDisable (textureTarget); // done with texturing |
// draw texel grid |
glColor3f (0.0f, 0.0f, 1.0f); // draw texel grid in blue |
if (pWindowInfo->grid) // if drawing is on |
{ |
long vertMod = effectiveTextureMod; // find the current mod to get to the effective texture size |
long horizMod = effectiveTextureMod; |
if (pWindowInfo->fTileTextures) // if we are tiling we have to account for odd size images |
{ // if the image size is odd then we know that we had to add an extra border pixel |
// account for this when drawing thegrid to ensure we draw the right amount of texels |
vertMod += (pWindowInfo->imageHeight & 0x01) ? 1 : 0; |
horizMod += (pWindowInfo->imageWidth & 0x01) ? 1 : 0; |
} |
// draw grid showing the actual texels draw to the screen each frame |
DrawGLTexelGrid (pWindowInfo->textureWidth - horizMod, pWindowInfo->textureHeight - vertMod, pWindowInfo->imageWidth, pWindowInfo->imageHeight, pWindowInfo->zoom); |
} |
glReportError (); // report any errors |
// draw polygon edges |
glColor3f (0.0f, 1.0f, 0.0f); // draw in green for polygon edges |
if (pWindowInfo->lines) // if edges turned on |
{ |
// offset x and y are used to draw the polygon and need to represent the texture effective edges (without borders) |
// so walk the texture size images adjusting for each border |
offsetX = 0; |
for (x = 0; x < pWindowInfo->textureX; x++) // for all horizontal textures |
{ |
// use remaining to determine next texture size |
currTextureWidth = GetNextTextureSize (pWindowInfo->textureWidth - offsetX, pWindowInfo->maxTextureSize, pWindowInfo->fNPOTTextures) - effectiveTextureMod; // current effective texture width for drawing |
offsetY = 0; // start at top |
for (y = 0; y < pWindowInfo->textureY; y++) // for a complete column |
{ |
// use remaining to determine next texture size |
currTextureHeight = GetNextTextureSize (pWindowInfo->textureHeight - offsetY, pWindowInfo->maxTextureSize, pWindowInfo->fNPOTTextures) - effectiveTextureMod; // effective texture height for drawing |
DrawGLImageTile (GL_LINE_STRIP, pWindowInfo->imageWidth, pWindowInfo->imageHeight, pWindowInfo->zoom, |
currTextureWidth, currTextureHeight, // draw this single texture on two tris |
offsetX, offsetY, |
pWindowInfo->fTileTextures ? currTextureWidth + offsetX : pWindowInfo->imageWidth, |
pWindowInfo->fTileTextures ? currTextureHeight + offsetY : pWindowInfo->imageHeight, |
pWindowInfo->fOverlapTextures, pWindowInfo->fNPOTTextures); |
offsetY += currTextureHeight; // offset drawing position for next texture vertically |
} |
offsetX += currTextureWidth; // offset drawing position for next texture horizontally |
} |
} |
glReportError (); // report any errors |
// draw info |
if (pWindowInfo->info) // if we are drawing the information overlay |
{ |
glLoadIdentity (); // reset model view matrix to identity (eliminates rotation basically) |
glScalef (2.0f / width, -2.0f / height, 1.0f); // scale to port per pixel scale |
glTranslatef (-(width) / 2.0f, -(height) / 2.0f, 0.0f); // translate center to upper left |
glColor3f (1.0f, 1.0f, 0.0f);// draw in yellow |
// center tick |
glBegin (GL_LINES); // draw center tick mark |
glVertex3f(width / 2.0f - 4.5f, height / 2.0f, 0.0f); |
glVertex3f(width / 2.0f + 4.5f, height / 2.0f, 0.0f); |
glVertex3f(width / 2.0f, height / 2.0f - 4.5f, 0.0f); |
glVertex3f(width / 2.0f, height / 2.0f + 4.5f, 0.0f); |
glEnd(); |
glRasterPos3d (10, yRaster++ * 12, 0); // set raster postion of bottom of text, incrementing for next line |
sprintf (cstr, "Image: (%ld x %ld x %ld)", |
pWindowInfo->imageWidth, pWindowInfo->imageHeight, pWindowInfo->imageDepth); |
DrawCStringGL (cstr, pWindowInfo->fontList); // draw image size and depth |
glRasterPos3d (10, yRaster++ * 12, 0); // set raster postion of bottom of text, incrementing for next line |
sprintf (cstr, "Textures: (%ld x %ld) Texels: (%ld x %ld)", |
pWindowInfo->textureX, pWindowInfo->textureY, pWindowInfo->textureWidth, pWindowInfo->textureHeight); |
DrawCStringGL (cstr, pWindowInfo->fontList); // draw texture info (1 x 1 with X texels for single texture code, X x Y for Z texels for tiled code |
if (pWindowInfo->fOverlapTextures) |
DrawCStringGL (" -overlapped-" , pWindowInfo->fontList); |
glRasterPos3d (10, yRaster++ * 12, 0); // set raster postion of bottom of text, incrementing for next line |
sprintf (cstr, "Window: (%ld x %ld)", |
width, height); |
DrawCStringGL (cstr, pWindowInfo->fontList); // draw window size |
glRasterPos3d (10, yRaster++ * 12, 0); // set raster postion of bottom of text, incrementing for next line |
sprintf (cstr, "Position: (%0.1f, %0.1f) at %0.1f deg", |
pWindowInfo->centerX / pWindowInfo->zoom, pWindowInfo->centerY / pWindowInfo->zoom, pWindowInfo->rotation); |
DrawCStringGL (cstr, pWindowInfo->fontList); // draw position information |
glRasterPos3d (10, yRaster++ * 12, 0); // set raster postion or bottom of text, incrementing for next line |
sprintf (cstr, "Zoom: %0.2f", |
pWindowInfo->zoom); |
DrawCStringGL (cstr, pWindowInfo->fontList); // draw zoom level |
yRaster = (height - 1) / 12; |
glRasterPos3d (10, yRaster-- * 12, 0); // set raster postion of bottom of text, incrementing for next line |
DrawCStringGL ((char*) glGetString (GL_RENDERER), pWindowInfo->fontList); // drag renderer string |
DrawCStringGL (", ", pWindowInfo->fontList); // drag version string |
DrawCStringGL ((char*) glGetString (GL_VERSION), pWindowInfo->fontList); // drag version string |
if (pWindowInfo->fClientTextures || pWindowInfo->fAGPTexturing) |
{ |
glRasterPos3d (10, yRaster-- * 12, 0); // set raster postion of bottom of text, incrementing for next line |
if (pWindowInfo->fClientTextures) |
DrawCStringGL ("<Client Storage> ", pWindowInfo->fontList); // draw zoom level |
if (pWindowInfo->fAGPTexturing) |
DrawCStringGL ("<AGP Texturing Hint>", pWindowInfo->fontList); // draw zoom level |
} |
// print frames per second and texturing rate |
if (pWindowInfo->spinning) |
{ |
AbsoluteTime currTime = UpTime (); |
float deltaTime = (float) AbsoluteDeltaToDuration (currTime, pWindowInfo->time); |
if (0 > deltaTime) // if negative microseconds |
deltaTime /= -1000000.0; |
else // else milliseconds |
deltaTime /= 1000.0; |
(pWindowInfo->frames)++; |
if (0.5 <= deltaTime) // has update interval passed |
{ |
double rate = pWindowInfo->frames / deltaTime; |
sprintf (pWindowInfo->cstrTime, "%0.0f FPS (%ld frames in %0.3f secs)", |
rate, |
pWindowInfo->frames, |
deltaTime); |
pWindowInfo->time = currTime; // reset for next time interval |
pWindowInfo->frames = 0; |
} |
glRasterPos3d (10, yRaster-- * 12, 0); // set raster postion or bottom of text, incrementing for next line |
DrawCStringGL (pWindowInfo->cstrTime, pWindowInfo->fontList); |
} |
glReportError (); |
} |
aglSwapBuffers (pWindowInfo->aglContext); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14