397 lines
16 KiB
C++
397 lines
16 KiB
C++
/*
|
|
* QTtexture.c
|
|
* Cocoa rostrumMIP
|
|
|
|
*
|
|
* Created by philatki on Thu Nov 29 2001.
|
|
* Copyright (c) 2001 __MyCompanyName__. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
PORTIONS OF THIS CODE ARE COPYRIGHT APPLE COMPUTER -
|
|
|
|
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__
|
|
#include <Carbon/Carbon.h>
|
|
#include <QuickTime/ImageCompression.h> // for image loading and decompression
|
|
#include <QuickTime/QuickTimeComponents.h> // for file type support
|
|
#else
|
|
#include <ImageCompression.h> // for image loading and decompression
|
|
#include <QuickTimeComponents.h> // for file type support
|
|
#endif
|
|
|
|
#include <osg/GL> // for OpenGL API
|
|
#include "QTUtils.h"
|
|
#include "QTtexture.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// ==================================
|
|
|
|
enum // how to scale image to power of two on read if scaling
|
|
{
|
|
kNone = 1,
|
|
kNearest, // find nearest power of 2
|
|
kNearestLess, // nearest power of 2 which is less than or equal image dimension
|
|
KNearestGreater, // nearest power of 2 which is greater than or equal image dimension
|
|
k32, // use this size specifically
|
|
k64,
|
|
k128,
|
|
k256,
|
|
k512,
|
|
k1024,
|
|
k2048,
|
|
k4096,
|
|
k8192
|
|
};
|
|
|
|
// Default values for images loading
|
|
short gTextureScale = k1024; // for non-tiled images the type of texture scaling to do
|
|
short gMaxTextureSize = 4096; // maximum texture size to use for application
|
|
Boolean gfTileTextures = true; // are multiple tiled textures used to display image?
|
|
Boolean gfOverlapTextures = true; // do tiled textures overlapped to create correct filtering between tiles? (only applies if using tiled textures)
|
|
Boolean gfClientTextures = false; // 10.1+ only: texture from client memory
|
|
Boolean gfAGPTextures = false; // 10.1+ only: texture from AGP memory without loading to GPU can be set after inmage loaded
|
|
Boolean gfNPOTTextures = false; // 10.1+ only: use Non-Power Of Two (NPOT) textures
|
|
|
|
// ---------------------------------
|
|
|
|
static long GetScaledTextureDimFromImageDim (long imageDimension, short scaling);
|
|
static unsigned char * LoadBufferFromImageFile (FSSpec fsspecImage, short imageScale,
|
|
long * pOrigImageWidth, long * pOrigImageHeight, long *pOrigDepth,
|
|
long * pBufferWidth, long * pBufferHeight, long * pBufferDepth);
|
|
|
|
// ---------------------------------
|
|
|
|
// based on scaling determine the texture dimension which fits the image dimension passe in
|
|
// kNone: no scaling just use image dimension (will not guarentee support power for 2 textures)
|
|
// kNearest: find nearest power of 2 texture
|
|
// kNearestLess: find nearest power of 2 texture which is less than image dimension
|
|
// kNearestGreater: find nearest power of 2 texture which is greater than image dimension
|
|
// k32 - k1024: use this specific texture size
|
|
|
|
static long GetScaledTextureDimFromImageDim (long imageDimension, short scaling)
|
|
{
|
|
switch (scaling)
|
|
{
|
|
case kNone: // no scaling
|
|
return imageDimension;
|
|
break;
|
|
case kNearest: // scale to nearest power of two
|
|
{
|
|
// find power of 2 greater
|
|
long i = 0, texDim = 1, imageTemp = imageDimension;
|
|
while (imageTemp >>= 1) // while the dimension still has bits of data shift right (losing a bit at a time)
|
|
i++; // count shifts (i.e., powers of two)
|
|
texDim = texDim << i; // shift our one bit representation left the like amount (i.e., 2 ^ i)
|
|
if (texDim >= gMaxTextureSize) // if we are too big or at max size
|
|
return gMaxTextureSize; // then must use max texture size
|
|
// are we closer to greater pow 2 or closer to higher pow 2?
|
|
// compare to the power of two that is double of initial guess
|
|
else if (((texDim << 1) - imageDimension) < (imageDimension - texDim))
|
|
return (texDim << 1); // if it is closer away then return double guess
|
|
else
|
|
return texDim; // otherwise orginal guess is closer so return this
|
|
}
|
|
break;
|
|
case kNearestLess:
|
|
{
|
|
// find power of 2 lower
|
|
long i = 0, texDim = 1;
|
|
while (imageDimension >>= 1) // while the dimension still has bits of data shift right (losing a bit at a time)
|
|
i++; // count shifts (i.e., powers of two)
|
|
texDim = texDim << i; // shift our one bit representation left the like amount (i.e., 2 ^ i)
|
|
return texDim; // returns the maxium power of two that is less than or equal the texture dimension
|
|
}
|
|
break;
|
|
case KNearestGreater:
|
|
{
|
|
// find power of 2 greater
|
|
long i = 0, texDim = 1;
|
|
while (imageDimension >>= 1) // while the dimension still has bits of data shift right (losing a bit at a time)
|
|
i++; // count shifts (i.e., powers of two)
|
|
texDim = texDim << (i + 1); // shift our one bit representation left the like amount (i.e., 2 ^ (i + 1))
|
|
return texDim; // returns the minimum power of two that is greater than or equal the texture dimension
|
|
}
|
|
break;
|
|
case k32: // return hard values for texture dimension
|
|
return 32;
|
|
break;
|
|
case k64:
|
|
return 64;
|
|
break;
|
|
case k128:
|
|
return 128;
|
|
break;
|
|
case k256:
|
|
return 256;
|
|
break;
|
|
case k512:
|
|
return 512;
|
|
break;
|
|
case k1024:
|
|
return 1024;
|
|
break;
|
|
case k2048:
|
|
return 2048;
|
|
break;
|
|
case k4096:
|
|
return 8192;
|
|
break;
|
|
case k8192:
|
|
return 8192;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static char errMess[256];
|
|
|
|
char *QTfailureMessage(void) { return errMess; }
|
|
|
|
static unsigned char * LoadBufferFromImageFile ( FSSpec fsspecImage,
|
|
short imageScale,
|
|
long *pOrigImageWidth, long *pOrigImageHeight, long *pOrigImageDepth,
|
|
long *pBufferWidth, long *pBufferHeight, long *pBufferDepth)
|
|
{
|
|
unsigned char * pImageBuffer = NULL;
|
|
GWorldPtr pGWorld = NULL;
|
|
OSType pixelFormat;
|
|
long rowStride; // length, in bytes, of a pixel row in the image
|
|
GraphicsImportComponent giComp; // componenet for importing image
|
|
Rect rectImage; // rectangle of source image
|
|
ImageDescriptionHandle hImageDesc; // handle to image description used to get image depth
|
|
MatrixRecord matrix;
|
|
GDHandle origDevice; // save field for current graphics device
|
|
CGrafPtr origPort; // save field for current graphics port
|
|
OSStatus err = noErr; // err return value
|
|
|
|
// zero output params
|
|
*pOrigImageWidth = 0;
|
|
*pOrigImageHeight = 0;
|
|
*pOrigImageDepth = 0;
|
|
*pBufferWidth = 0;
|
|
*pBufferHeight = 0;
|
|
*pBufferDepth = 0;
|
|
|
|
// get imorter for the image tyoe in file
|
|
GetGraphicsImporterForFile (&fsspecImage, &giComp);
|
|
if (err != noErr) { // if we have an error
|
|
sprintf ( errMess, "couldnt find importer\n");
|
|
return NULL; // go away
|
|
}
|
|
|
|
// Create GWorld
|
|
err = GraphicsImportGetNaturalBounds (giComp, &rectImage); // get the image bounds
|
|
if (err != noErr) {
|
|
sprintf ( errMess, "failed to GraphicsImportGetNaturalBounds");
|
|
|
|
return NULL; // go away if error
|
|
}
|
|
// create a handle for the image description
|
|
hImageDesc = (ImageDescriptionHandle) NewHandle (sizeof (ImageDescriptionHandle));
|
|
HLock ((Handle) hImageDesc); // lock said handle
|
|
err = GraphicsImportGetImageDescription (giComp, &hImageDesc); // retrieve the image description
|
|
if (err != noErr) {
|
|
sprintf ( errMess, "failed to GraphicsImportGetImageDescription");
|
|
|
|
return NULL; // go away if error
|
|
}
|
|
*pOrigImageWidth = (long) (rectImage.right - rectImage.left); // find width from right side - left side bounds
|
|
*pOrigImageHeight = (long) (rectImage.bottom - rectImage.top); // same for height
|
|
|
|
// we will use a 24-bit rgb texture or a 32-bit rgba
|
|
if ((**hImageDesc).depth == 32) *pOrigImageDepth=4;
|
|
else *pOrigImageDepth=3;
|
|
|
|
*pBufferDepth = 32; // we will use a 32 bbit texture (this includes 24 bit images)
|
|
pixelFormat = k32ARGBPixelFormat;
|
|
|
|
bool doScaling = false;
|
|
if (doScaling)
|
|
{
|
|
int scalefac;
|
|
// note - we want texels to stay square, so
|
|
if ((*pOrigImageWidth) > (*pOrigImageHeight))
|
|
{
|
|
*pBufferWidth = GetScaledTextureDimFromImageDim ( *pOrigImageWidth, imageScale ) ;
|
|
*pBufferHeight=*pBufferWidth;
|
|
scalefac = X2Fix ((float) (*pBufferWidth) / (float) *pOrigImageWidth);
|
|
}
|
|
else
|
|
{
|
|
*pBufferHeight = GetScaledTextureDimFromImageDim (*pOrigImageHeight, imageScale );
|
|
*pBufferWidth = *pBufferHeight;
|
|
scalefac = X2Fix ((float) (*pBufferHeight) / (float) *pOrigImageHeight);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// NOTE: scaling of the image removed, this is already done inside osg::Image
|
|
*pBufferWidth = *pOrigImageWidth;
|
|
*pBufferHeight= *pOrigImageHeight;
|
|
}
|
|
|
|
SetRect (&rectImage, 0, 0, (short) *pBufferWidth, (short) *pBufferHeight); // l, t, r. b set image rectangle for creation of GWorld
|
|
rowStride = *pBufferWidth * *pBufferDepth >> 3; // set stride in bytes width of image * pixel depth in bytes
|
|
|
|
const long len = rowStride * *pBufferHeight;
|
|
|
|
pImageBuffer = new unsigned char [ len ]; // build new buffer exact size of image (stride * height)
|
|
|
|
// pImageBuffer = (unsigned char *) NewPtrClear (rowStride * *pBufferHeight); // build new buffer exact size of image (stride * height)
|
|
|
|
|
|
if (NULL == pImageBuffer)
|
|
{
|
|
sprintf ( errMess, "failed to allocate image buffer");
|
|
CloseComponent(giComp); // dump component
|
|
return NULL; // if we failed to allocate buffer
|
|
}
|
|
// create a new gworld using our unpadded buffer, ensure we set the pixel type correctly for the expected image bpp
|
|
QTNewGWorldFromPtr (&pGWorld, pixelFormat, &rectImage, NULL, NULL, 0, pImageBuffer, rowStride);
|
|
if (NULL == pGWorld)
|
|
{
|
|
sprintf ( errMess, "failed to create GWorld");
|
|
// DisposePtr ((Ptr) pImageBuffer); // dump image buffer
|
|
delete [] pImageBuffer;
|
|
pImageBuffer = NULL;
|
|
CloseComponent(giComp);
|
|
return NULL; // if we failed to create gworld
|
|
}
|
|
|
|
GetGWorld (&origPort, &origDevice); // save onscreen graphics port
|
|
|
|
// decompress (draw) to gworld and thus fill buffer
|
|
SetIdentityMatrix (&matrix); // set transform matrix to identity (basically pass through)
|
|
|
|
TranslateMatrix ( &matrix, -X2Fix(0.5f * *pOrigImageWidth), -X2Fix(0.5f * *pOrigImageHeight));
|
|
ScaleMatrix (&matrix, X2Fix(1.0), X2Fix(-1.0), X2Fix (0.0), X2Fix (0.0));
|
|
TranslateMatrix ( &matrix, X2Fix(0.5f * *pOrigImageWidth), X2Fix(0.5f * *pOrigImageHeight));
|
|
|
|
err = GraphicsImportSetMatrix(giComp, &matrix); // set our matrix as the importer matrix
|
|
|
|
if (err == noErr)
|
|
err = GraphicsImportSetGWorld (giComp, pGWorld, NULL); // set the destination of the importer component
|
|
if (err == noErr)
|
|
err = GraphicsImportSetQuality (giComp, codecLosslessQuality); // we want lossless decompression
|
|
if ((err == noErr) && GetGWorldPixMap (pGWorld) && LockPixels (GetGWorldPixMap (pGWorld)))
|
|
GraphicsImportDraw (giComp); // if everything looks good draw image to locked pixmap
|
|
else
|
|
{
|
|
sprintf ( errMess, "failed to Set Matrix or GWorld or Quality or GetPixMap");
|
|
|
|
DisposeGWorld (pGWorld); // dump gworld
|
|
pGWorld = NULL;
|
|
// DisposePtr ((Ptr) pImageBuffer); // dump image buffer
|
|
delete [] pImageBuffer;
|
|
pImageBuffer = NULL;
|
|
CloseComponent(giComp); // dump component
|
|
return NULL;
|
|
}
|
|
|
|
UnlockPixels (GetGWorldPixMap (pGWorld)); // unlock pixels
|
|
CloseComponent(giComp); // dump component
|
|
SetGWorld(origPort, origDevice); // set current graphics port to offscreen
|
|
// done with gworld and image since they are loaded to a texture
|
|
// DisposeGWorld (pGWorld); // do not need gworld
|
|
// pGWorld = NULL;
|
|
|
|
return pImageBuffer;
|
|
}
|
|
|
|
// new implementation of darwinPathToFSSpec
|
|
// the old code fails for me under os x 10.1.5
|
|
// the code below is from an example of apple, modified to fit our needs
|
|
// the example can be found at
|
|
// <http://developer.apple.com/samplecode/Sample_Code/Files/MoreFilesX.htm>
|
|
|
|
FSSpec *darwinPathToFSSpec (char *fname ) {
|
|
|
|
FSSpec *fs;
|
|
OSStatus result;
|
|
#if defined( __APPLE__ )
|
|
FSRef ref;
|
|
|
|
result = FSPathMakeRef( (UInt8*)fname, &ref, false); // fname is not a directory
|
|
if (result!=0) return NULL;
|
|
|
|
/* and then convert the FSRef to an FSSpec */
|
|
fs = (FSSpec *) malloc(sizeof(FSSpec));
|
|
result = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, fs, NULL);
|
|
if (result==0) return fs; // success
|
|
|
|
// failed:
|
|
free(fs);
|
|
return NULL;
|
|
#else
|
|
// windows implementation to get a fsspec
|
|
fs = (FSSpec *) malloc(sizeof(FSSpec));
|
|
result = NativePathNameToFSSpec(fname, fs, 0 /* flags */);
|
|
|
|
if (0 == result)
|
|
return fs;
|
|
free(fs);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
unsigned char*
|
|
LoadBufferFromDarwinPath ( const char *fname, long *origWidth, long *origHeight, long *origDepth,
|
|
long *buffWidth, long *buffHeight,
|
|
long *buffDepth)
|
|
{
|
|
FSSpec *fs;
|
|
|
|
fs=darwinPathToFSSpec ( const_cast<char*>( fname ) );
|
|
|
|
if (fs == NULL) {
|
|
sprintf ( errMess, "error creating path from fsspec" );
|
|
return NULL;
|
|
}
|
|
else
|
|
return LoadBufferFromImageFile ( *fs, kNone, origWidth,origHeight,origDepth,buffWidth,buffHeight,buffDepth);
|
|
}
|