TQ3TextureObject QutTexture_CreateTextureObjectFromPixmap(PixMapHandle thePixMap, TQ3PixelType pixelType, TQ3Boolean wantMipMaps) { TQ3Uns32 x, y, theWidth, theHeight, rowBytes, pixelBytes; TQ3TextureObject qd3dTextureObject = NULL; TQ3StorageObject qd3dMemoryStorage; TQ3StoragePixmap qd3dPixMap; TQ3Mipmap qd3dMipMap; UInt16 *pixelPtr; UInt8 *baseAddr; OSType pixelFormat; TQ3Endian byteOrder; // Get the details we need from the PixMap NoPurgePixels(thePixMap); LockPixels(thePixMap); theWidth = (*thePixMap)->bounds.right - (*thePixMap)->bounds.left; theHeight = (*thePixMap)->bounds.bottom - (*thePixMap)->bounds.top; rowBytes = (*thePixMap)->rowBytes & 0x7FFF; pixelBytes = (*thePixMap)->pixelSize / 8; baseAddr = (UInt8 *) GetPixBaseAddr(thePixMap); #if OLDPIXMAPSTRUCT pixelFormat = 0; #else pixelFormat = (*thePixMap)->pixelFormat; #endif // If this is a 16 bit alpha channel texture, set the alpha bits. // We assume that black is transparent. if (pixelType == kQ3PixelTypeARGB16) { for (y = 0; y < theHeight; y++) { for (x = 0; x < theWidth; x++) { pixelPtr = (UInt16 *) (baseAddr + (y * rowBytes) + (x * 2)); if (*pixelPtr != 0x0000) *pixelPtr |= (1 << 15); } } } // Set the byte order if ( (pixelFormat == k32BGRAPixelFormat) || (pixelFormat == k16LE555PixelFormat) ) { byteOrder = kQ3EndianLittle; } else { byteOrder = kQ3EndianBig; } // Create a storage object based on the GWorld qd3dMemoryStorage = Q3MemoryStorage_New(baseAddr, theHeight * rowBytes); if (qd3dMemoryStorage != NULL) { // Create the appropriate type of texture. Note that if mip-maps are // required, we create a QD3D PixMap. This might seem back to front, // but QD3D automatically creates mip-maps for PixMaps. // // If mip-maps are not required, we create a QD3D MipMap by hand and // signal that we don't want any other mip-maps to be created. // // We need to work around a bug in Q3MipmapTexture_New: if the rowByte // for the pixel exactly matches the size of each row, the texture is // distorted - we can fix this by using Q3PixmapTexture_New instead. if (wantMipMaps || (rowBytes == theWidth * pixelBytes)) { // Create a PixMap from the GWorld data qd3dPixMap.image = qd3dMemoryStorage; qd3dPixMap.width = theWidth; qd3dPixMap.height = theHeight; qd3dPixMap.rowBytes = rowBytes; qd3dPixMap.pixelSize = (pixelType == kQ3PixelTypeARGB32 || pixelType == kQ3PixelTypeRGB32) ? 32 : 16; qd3dPixMap.pixelType = pixelType; qd3dPixMap.bitOrder = byteOrder; qd3dPixMap.byteOrder = byteOrder; qd3dTextureObject = Q3PixmapTexture_New(&qd3dPixMap); } else { // Create a MipMap from the GWorld data qd3dMipMap.image = qd3dMemoryStorage; qd3dMipMap.useMipmapping = kQ3False; qd3dMipMap.pixelType = pixelType; qd3dMipMap.bitOrder = byteOrder; qd3dMipMap.byteOrder = byteOrder; qd3dMipMap.reserved = 0; qd3dMipMap.mipmaps[0].width = theWidth; qd3dMipMap.mipmaps[0].height = theHeight; qd3dMipMap.mipmaps[0].rowBytes = rowBytes; qd3dMipMap.mipmaps[0].offset = 0; qd3dTextureObject = Q3MipmapTexture_New(&qd3dMipMap); } Q3Object_Dispose(qd3dMemoryStorage); } // Clean up and return UnlockPixels(thePixMap); return(qd3dTextureObject); }
short LoadMapPICT( PicHandle pict, unsigned long mapID, unsigned long mapSizeX, unsigned long mapSizeY, TQ3StoragePixmap *bMap) { unsigned long *textureMap; unsigned long *textureMapAddr; unsigned long *pictMap; unsigned long pictMapAddr; register unsigned long row; register unsigned long col; Rect rectGW; GWorldPtr pGWorld; PixMapHandle hPixMap; unsigned long pictRowBytes; QDErr err; GDHandle oldGD; GWorldPtr oldGW; short success; mapID; /* unused argument */ /* save current port */ GetGWorld(&oldGW, &oldGD); /* create the GWorld */ SetRect(&rectGW, 0, 0, (unsigned short)mapSizeX, (unsigned short)mapSizeY); err = NewGWorld(&pGWorld, 32, &rectGW, 0, 0, useTempMem); if (err != noErr) return 0; success = 1; hPixMap = GetGWorldPixMap(pGWorld); pictMapAddr = (unsigned long)GetPixBaseAddr (hPixMap); pictRowBytes = (unsigned long)(**hPixMap).rowBytes & 0x3fff; /* put the PICT into the window */ SetGWorld(pGWorld, nil); LockPixels(hPixMap); EraseRect(&rectGW); DrawPicture(pict, &rectGW); /* allocate an area of memory for the texture */ textureMap = (unsigned long *)malloc(mapSizeX * mapSizeY * sizeof(unsigned long)); if (textureMap == NULL) { success = 0; goto bail; } /* bMap->image = (char *)textureMap; */ /* copy the PICT into the texture */ textureMapAddr = textureMap; for (row = 0L; row < mapSizeY; row++) { pictMap = (unsigned long *)(pictMapAddr + (pictRowBytes * row)); for (col = 0L; col < mapSizeX; col++) { *textureMap++ = (*pictMap++ | 0xff000000L); } } bMap->image = Q3MemoryStorage_New((const unsigned char *)textureMapAddr, mapSizeX * mapSizeY * sizeof(unsigned long)); if (bMap->image == NULL) { /* error */ success = 0; goto bail; } UnlockPixels(hPixMap); bMap->width = mapSizeX; bMap->height = mapSizeY; bMap->rowBytes = bMap->width * 4; bMap->pixelSize = 32; bMap->pixelType = kQ3PixelTypeRGB32; bMap->bitOrder = kQ3EndianBig; bMap->byteOrder = kQ3EndianBig; /* Free junk */ bail: SetGWorld(oldGW, oldGD); DisposeGWorld(pGWorld); if (textureMapAddr != NULL) free(textureMapAddr); return success; }
//----------------------------------------------------------------------------- // QutTexture_CreateTextureObjectFromTGAFile : Create texture object from a TGA file. //----------------------------------------------------------------------------- TQ3TextureObject QutTexture_CreateTextureObjectFromTGAFile( const char* inFilePath ) { TQ3TextureObject theTexture = NULL; FILE* theFile = NULL; TGAHeader theHeader; int numPixels, bytesPerPixel, n, i, j; int oppositeRow, packetHeader, chunkSize, rowBytes; TQ3Object memStorage = NULL; unsigned char p[5]; unsigned char* theBuffer; TQ3Uns32 bufferSize; TQ3StoragePixmap thePixMap; TQ3PixelType pixelType; theFile = fopen( inFilePath, "rb" ); if (theFile != NULL) { // Read the file header. theHeader.IDLength = fgetc( theFile ); theHeader.colorMapType = fgetc( theFile ); theHeader.dataTypeCode = fgetc( theFile ); theHeader.colorMapOrigin = qutTexture_read_littleendian_short( theFile ); theHeader.colorMapLength = qutTexture_read_littleendian_short( theFile ); theHeader.colorMapDepth = fgetc( theFile ); theHeader.x_origin = qutTexture_read_littleendian_short( theFile ); theHeader.y_origin = qutTexture_read_littleendian_short( theFile ); theHeader.width = qutTexture_read_littleendian_short( theFile ); theHeader.height = qutTexture_read_littleendian_short( theFile ); theHeader.bitsPerPixel = fgetc( theFile ); theHeader.imageDescriptor = fgetc( theFile ); // Check that the image format is one we can handle. if ( ( (theHeader.dataTypeCode == kTGATypeColor) || (theHeader.dataTypeCode == kTGATypeColorRLE) ) && ( (theHeader.bitsPerPixel == 16) || (theHeader.bitsPerPixel == 24) || (theHeader.bitsPerPixel == 32) ) && ( (theHeader.colorMapType == 0) || (theHeader.colorMapType == 1) ) && ! (feof(theFile) || ferror(theFile)) ) { // Skip identification field, if any. fseek( theFile, theHeader.IDLength, SEEK_CUR ); // Skip color map, if any. fseek( theFile, theHeader.colorMapType * theHeader.colorMapLength, SEEK_CUR ); // Make a storage object to hold the pixels. bytesPerPixel = theHeader.bitsPerPixel / 8; numPixels = theHeader.width * theHeader.height; bufferSize = numPixels * bytesPerPixel; theBuffer = (unsigned char*) malloc( bufferSize ); if (theBuffer != NULL) { // Read the image. n = 0; while ( (n < numPixels) && (!feof(theFile)) && (!ferror(theFile)) ) { if (theHeader.dataTypeCode == kTGATypeColor) // uncompressed { fread( theBuffer + n * bytesPerPixel, bytesPerPixel, 1, theFile ); ++n; } else // RLE compressed { packetHeader = fgetc( theFile ); chunkSize = (packetHeader & 0x7F) + 1; if (packetHeader & 0x80) // RLE chunk { fread( p, bytesPerPixel, 1, theFile ); for (i=0; i < chunkSize; ++i) { memcpy( theBuffer + n * bytesPerPixel, p, bytesPerPixel ); ++n; } } else // uncompressed chunk { fread( theBuffer + n * bytesPerPixel, bytesPerPixel, chunkSize, theFile ); n += chunkSize; } } } // We may need to flip the image into the usual top to bottom row order. rowBytes = theHeader.width * bytesPerPixel; if ( (theHeader.imageDescriptor & kTGADescTopToBottom) == 0) { for (i = 0; i < theHeader.height / 2; ++i) { oppositeRow = theHeader.height - i - 1; for (j = 0; j < rowBytes; ++j) { unsigned char temp = theBuffer[ i * rowBytes + j ]; theBuffer[ i * rowBytes + j ] = theBuffer[ oppositeRow * rowBytes + j ]; theBuffer[ oppositeRow * rowBytes + j ] = temp; } } } // Set the pixel type switch (bytesPerPixel) { case 2: pixelType = kQ3PixelTypeRGB16; break; case 3: pixelType = kQ3PixelTypeRGB24; break; default: // quiet an uninitialized-variable warning case 4: pixelType = kQ3PixelTypeARGB32; // Typically TGA format uses non-premultiplied alpha, // whereas Quesa usually expects premultiplied alpha, so // we multiply here. for (i = 0; i < theHeader.width * theHeader.height; ++i) { unsigned short a = theBuffer[i*4+3]; theBuffer[i*4] = (theBuffer[i*4]*a) / 255; theBuffer[i*4+1] = (theBuffer[i*4+1]*a) / 255; theBuffer[i*4+2] = (theBuffer[i*4+2]*a) / 255; } break; } #if TARGET_API_MAC_OS8 // QD3D on Mac does not support kQ3PixelTypeRGB24, and also appears to // ignore the byte order, so we must convert to kQ3PixelTypeRGB32 in // big-endian order. if (pixelType == kQ3PixelTypeRGB24) { unsigned char* bigBuffer = malloc( numPixels * 4 ); if (bigBuffer != NULL) { pixelType = kQ3PixelTypeRGB32; for (i = 0; i < numPixels; ++i) { bigBuffer[4*i + 3] = theBuffer[3*i]; bigBuffer[4*i + 2] = theBuffer[3*i + 1]; bigBuffer[4*i + 1] = theBuffer[3*i + 2]; } bytesPerPixel = 4; rowBytes = theHeader.width * bytesPerPixel; bufferSize = numPixels * bytesPerPixel; free( theBuffer ); theBuffer = bigBuffer; } } #endif // Create a memory storage object holding a copy of the buffer memStorage = Q3MemoryStorage_New( theBuffer, bufferSize ); if (memStorage != NULL) { // Create the texture object thePixMap.image = memStorage; thePixMap.width = theHeader.width; thePixMap.height = theHeader.height; thePixMap.rowBytes = rowBytes; thePixMap.pixelSize = bytesPerPixel * 8; thePixMap.bitOrder = kQ3EndianLittle; thePixMap.byteOrder = kQ3EndianLittle; thePixMap.pixelType = pixelType; theTexture = Q3PixmapTexture_New( &thePixMap ); Q3Object_Dispose( memStorage ); } free( theBuffer ); } } fclose( theFile ); } return theTexture; }
TQ3Status E3CompressedPixmapTexture_CompressImage(TQ3CompressedPixmap * compressedPixmap, PixMapHandle sourcePixMap, CodecType codecType, CodecComponent codecComponent, TQ3Int16 codedDepth, CodecQ codecQuality) { // If we support QuickTime, compress the image ImageDescriptionHandle imageDescH = NULL; long maxCompressedSize = 0; Handle compressedDataH = NULL; Ptr compressedDataP = NULL; OSErr theErr = noErr; Rect bounds = (**sourcePixMap).bounds; TQ3StorageObject compressedImage = NULL; TQ3StorageObject imageDesc = NULL; // Make sure QuickTime is present if ((TQ3Uns32) EnterMovies == (TQ3Uns32) kUnresolvedCFragSymbolAddress) return(kQ3Failure); theErr = GetMaxCompressionSize( sourcePixMap, &bounds, codedDepth, codecQuality, codecType, (CompressorComponent)codecComponent, &maxCompressedSize); if ( theErr != noErr ) { // paramErr or noCodecErr E3ErrorManager_PostError( kQ3ErrorInvalidParameter, kQ3False ) ; // failure return(kQ3Failure); } // allocate memory - we need to use Mac OS Handles for QuickTime imageDescH = (ImageDescriptionHandle) NewHandle( 4 ); compressedDataH = NewHandle( maxCompressedSize ); if ( compressedDataH != NULL && imageDescH != NULL ) { HLock(compressedDataH); compressedDataP = *compressedDataH; theErr = FCompressImage(sourcePixMap, &bounds, codedDepth, codecQuality, codecType, (CompressorComponent) codecComponent, NULL, 0, 0, NULL, NULL, imageDescH, compressedDataP); if ( theErr != noErr ) { // post error if (MemError() != noErr) E3ErrorManager_PostError( kQ3ErrorOutOfMemory , kQ3False ) ; else E3ErrorManager_PostError( kQ3ErrorInvalidParameter , kQ3False ) ; //deallocate handle storage DisposeHandle( (Handle)imageDescH); DisposeHandle( compressedDataH); // failure return(kQ3Failure) ; } } // otherwise we have a memory error else { // deallocate handle storage if (imageDescH) DisposeHandle( (Handle)imageDescH); if (compressedDataH) DisposeHandle( compressedDataH); // post error E3ErrorManager_PostError( kQ3ErrorOutOfMemory , kQ3False ) ; //failure return( kQ3Failure) ; } if (imageDescH) DisposeHandle( (Handle)imageDescH); if (compressedDataH) DisposeHandle( compressedDataH); // lock the image desc handle HLock( (Handle) imageDescH ) ; // store the data in storage objects compressedImage = Q3MemoryStorage_New( (unsigned char *) compressedDataP, (TQ3Uns32) (**imageDescH).dataSize ) ; imageDesc = Q3MemoryStorage_New( (unsigned char *) imageDescH, (TQ3Uns32) (**imageDescH).idSize ) ; // make sure memory was allocated if( compressedImage == NULL && imageDesc == NULL ) { if( compressedImage != NULL ) Q3Object_Dispose( compressedImage ) ; if( imageDesc != NULL ) Q3Object_Dispose( imageDesc ) ; // deallocate handle storage DisposeHandle( (Handle) imageDescH ) ; DisposeHandle( compressedDataH ) ; // we don't need to post a memory error because it // has already been done, return failure return(kQ3Failure); } // store the data in the compressed pixmap structure E3Shared_Acquire(&compressedPixmap->compressedImage, compressedImage); E3Shared_Acquire(&compressedPixmap->imageDesc, imageDesc); // NOTE: we do not fill out the other fields of the data structure, // since this is the defined QD3D behaviour // deallocate handle storage DisposeHandle( (Handle) imageDescH ) ; DisposeHandle( compressedDataH ) ; return(kQ3Success) ; }