1
0
mirror of https://github.com/thes3m/XNI synced 2024-12-26 13:26:06 +01:00

Added support for .pvr textures. (Make sure you use texture tools as described in apple documentation)

This commit is contained in:
Samo Pajk 2013-09-18 12:36:18 +02:00
parent 0dfd64fc9c
commit 788b374269
9 changed files with 250 additions and 54 deletions

View File

@ -106,7 +106,7 @@
if ([extension isEqualToString:@"png"] || [extension isEqualToString:@"jpg"] || [extension isEqualToString:@"jpeg"] ||
[extension isEqualToString:@"gif"] || [extension isEqualToString:@"tif"] || [extension isEqualToString:@"tiff"] ||
[extension isEqualToString:@"ico"] || [extension isEqualToString:@"bmp"]) {
[extension isEqualToString:@"ico"] || [extension isEqualToString:@"bmp"] || [extension isEqualToString:@"pvr"]) {
// Texture content
if (!importer) {
importer = [[[TextureImporter alloc] init] autorelease];

View File

@ -12,7 +12,7 @@
@interface PixelBitmapContent : BitmapContent {
SurfaceFormat format;
int bytesPerPixel;
float bytesPerPixel;
}
- (id) initWithWidth:(int)theWidth height:(int)theHeight format:(SurfaceFormat)theFormat;

View File

@ -31,13 +31,13 @@
- (Byte *) getPixelAtX:(int)x Y:(int)y {
// Index into the data array at bytesPerPixel intervals.
Byte *bytes = (Byte*)[pixelData bytes];
return &bytes[(x + y * width) * bytesPerPixel];
return &bytes[(int)((x + y * width) * bytesPerPixel)];
}
- (void) setPixelAtX:(int)x Y:(int)y Value:(Byte *)value {
// The value contains bytesPerPixel bytes.
Byte *bytes = (Byte*)[pixelData bytes];
memcpy(&bytes[(x + y * width) * bytesPerPixel], value, bytesPerPixel);
memcpy(&bytes[(int)((x + y * width) * bytesPerPixel)], value, bytesPerPixel);
}
@end

View File

@ -15,7 +15,7 @@
}
+ (BOOL) tryGetSizeInBytesOfSurfaceFormat:(SurfaceFormat)surfaceFormat sizeInBytes:(int*)sizeInBytes;
+ (BOOL) tryGetSizeInBytesOfSurfaceFormat:(SurfaceFormat)surfaceFormat sizeInBytes:(float*)sizeInBytes;
+ (BOOL) tryGetVectorTypeOfSurfaceFormat:(SurfaceFormat)surfaceFormat vectorType:(Class*)type;

View File

@ -13,7 +13,7 @@
@implementation VectorConverter
+ (BOOL) tryGetSizeInBytesOfSurfaceFormat:(SurfaceFormat)surfaceFormat sizeInBytes:(int*)sizeInBytes {
+ (BOOL) tryGetSizeInBytesOfSurfaceFormat:(SurfaceFormat)surfaceFormat sizeInBytes:(float*)sizeInBytes {
switch (surfaceFormat) {
case SurfaceFormatColor:
*sizeInBytes = sizeof(Byte4);
@ -29,7 +29,15 @@
return YES;
case SurfaceFormatAlpha8:
*sizeInBytes = sizeof(Alpha8);
return YES;
return YES;
case SurfaceFormatPvrtc4bAlpha:
case SurfaceFormatPvrtc4b:
*sizeInBytes = 0.5f;
return YES;
case SurfaceFormatPvrtc2bAlpha:
case SurfaceFormatPvrtc2b:
*sizeInBytes = 0.25f;
return YES;
default:
break;
}

View File

@ -13,49 +13,202 @@
#import "Retronator.Xni.Framework.Content.Pipeline.h"
#import "Retronator.Xni.Framework.Content.Pipeline.Graphics.h"
#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff
static char gPVRTexIdentifier[4] = "PVR!";
enum
{
kPVRTextureFlagTypePVRTC_2 = 24,
kPVRTextureFlagTypePVRTC_4
};
typedef struct _PVRTexHeader
{
uint32_t headerLength;
uint32_t height;
uint32_t width;
uint32_t numMipmaps;
uint32_t flags;
uint32_t dataLength;
uint32_t bpp;
uint32_t bitmaskRed;
uint32_t bitmaskGreen;
uint32_t bitmaskBlue;
uint32_t bitmaskAlpha;
uint32_t pvrTag;
uint32_t numSurfs;
} PVRTexHeader;
@implementation TextureImporter
- (TextureContent*) importFile:(NSString*)filename {
NSData *textureData = [NSData dataWithContentsOfFile:filename];
UIImage *image = [UIImage imageWithData:textureData];
if (image == nil) {
[NSException raise:@"InvalidArgumentException" format:@"The provided file is not a supported texture resource."];
}
// Allocate space for raw data.
GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc(width * height * 4);
- (TextureContent*) importFile:(NSString*)filename {
NSData *textureData = [NSData dataWithContentsOfFile:filename];
// Create a context for drawing to the raw data space.
CGContextRef textureContext = CGBitmapContextCreate(imageData, width, height, 8, 4 * width, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
if ([[filename pathExtension] isEqualToString:@"pvr"]) {
//Import pvrtc texture
NSMutableArray *images = [NSMutableArray array];
SurfaceFormat format;
int width;
int height;
[self unpackPVR:textureData to:images format:&format width:&width height:&height];
if (images.count == 0) {
[NSException raise:@"InvalidArgumentException" format:@"Make sure that .pvr texture is properly compressed and that the file has correct header. Use this command in terminal:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/texturetool -e PVRTC -f PVR --channel-weighting-perceptual --bits-per-pixel-4 -o output.pvr input.png"];
}
// This bitmap is the only one in the mipmap chain.
MipmapChain *mipmaps = [[[MipmapChain alloc] init] autorelease];
for(NSData *imgData in images){
PixelBitmapContent *bitmap = [[PixelBitmapContent alloc] initWithWidth:(int)width height:(int)height format:format];
NSLog(@"Storing compressed texture with data length:%d", [imgData length]);
[bitmap setPixelData:[NSData dataWithBytes:[imgData bytes] length:[imgData length]]];
[mipmaps addObject:bitmap];
[bitmap release];
}
// Create the texture content.
Texture2DContent *content = [[[Texture2DContent alloc] init] autorelease];
content.identity.sourceFilename = filename;
content.mipmaps = mipmaps;
return content;
}else{
UIImage *image = [UIImage imageWithData:textureData];
if (image == nil) {
[NSException raise:@"InvalidArgumentException" format:@"The provided file is not a supported texture resource."];
}
// Allocate space for raw data.
GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc(width * height * 4);
// Create a context for drawing to the raw data space.
CGContextRef textureContext = CGBitmapContextCreate(imageData, width, height, 8, 4 * width, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
// Draw the image to our context.
CGContextClearRect(textureContext, CGRectMake(0, 0, width, height));
CGContextTranslateCTM(textureContext, 0, 0);
CGContextDrawImage(textureContext, CGRectMake(0, 0, width, height), image.CGImage);
// Create pixel bitmap content.
PixelBitmapContent *bitmap = [[[PixelBitmapContent alloc] initWithWidth:(int)width height:(int)height format:SurfaceFormatColor] autorelease];
[bitmap setPixelData:[NSData dataWithBytes:imageData length:width*height*4]];
// Clean up.
CGColorSpaceRelease(colorSpace);
CGContextRelease(textureContext);
free(imageData);
// This bitmap is the only one in the mipmap chain.
MipmapChain *mipmaps = [[[MipmapChain alloc] init] autorelease];
[mipmaps addObject:bitmap];
// Create the texture content.
Texture2DContent *content = [[[Texture2DContent alloc] init] autorelease];
content.identity.sourceFilename = filename;
content.mipmaps = mipmaps;
return content;
// Draw the image to our context.
CGContextClearRect(textureContext, CGRectMake(0, 0, width, height));
CGContextTranslateCTM(textureContext, 0, 0);
CGContextDrawImage(textureContext, CGRectMake(0, 0, width, height), image.CGImage);
// Create pixel bitmap content.
PixelBitmapContent *bitmap = [[[PixelBitmapContent alloc] initWithWidth:(int)width height:(int)height format:SurfaceFormatColor] autorelease];
[bitmap setPixelData:[NSData dataWithBytes:imageData length:width*height*4]];
// Clean up.
CGColorSpaceRelease(colorSpace);
CGContextRelease(textureContext);
free(imageData);
// This bitmap is the only one in the mipmap chain.
MipmapChain *mipmaps = [[[MipmapChain alloc] init] autorelease];
[mipmaps addObject:bitmap];
// Create the texture content.
Texture2DContent *content = [[[Texture2DContent alloc] init] autorelease];
content.identity.sourceFilename = filename;
content.mipmaps = mipmaps;
return content;
}
}
- (BOOL) unpackPVR:(NSData*)data to:(NSMutableArray*)imageData format:(GLenum*)formatOut width:(int*)widthOut height:(int*)heightOut{
BOOL success = FALSE;
PVRTexHeader *header = NULL;
uint32_t flags, pvrTag;
uint32_t dataLength = 0, dataOffset = 0, dataSize = 0;
uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
uint32_t width = 0, height = 0, bpp = 4;
uint8_t *bytes = NULL;
uint32_t formatFlags;
header = (PVRTexHeader *)[data bytes];
pvrTag = CFSwapInt32LittleToHost(header->pvrTag);
if (gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) ||
gPVRTexIdentifier[1] != ((pvrTag >> 8) & 0xff) ||
gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) ||
gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff))
{
return FALSE;
}
flags = CFSwapInt32LittleToHost(header->flags);
formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;
if (formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2)
{
[imageData removeAllObjects];
BOOL alpha = FALSE;
if (CFSwapInt32LittleToHost(header->bitmaskAlpha))
alpha = TRUE;
else
alpha = FALSE;
if (formatFlags == kPVRTextureFlagTypePVRTC_4 && alpha)
*formatOut = SurfaceFormatPvrtc4bAlpha;
else if (formatFlags == kPVRTextureFlagTypePVRTC_4 && !alpha)
*formatOut = SurfaceFormatPvrtc4b;
else if (formatFlags == kPVRTextureFlagTypePVRTC_2 && alpha)
*formatOut = SurfaceFormatPvrtc2bAlpha;
else if (formatFlags == kPVRTextureFlagTypePVRTC_2 && !alpha)
*formatOut = SurfaceFormatPvrtc2b;
*widthOut = width = CFSwapInt32LittleToHost(header->width);
*heightOut = height = CFSwapInt32LittleToHost(header->height);
dataLength = CFSwapInt32LittleToHost(header->dataLength);
bytes = ((uint8_t *)[data bytes]) + sizeof(PVRTexHeader);
// Calculate the data size for each texture level and respect the minimum number of blocks
while (dataOffset < dataLength)
{
if (formatFlags == kPVRTextureFlagTypePVRTC_4)
{
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4;
heightBlocks = height / 4;
bpp = 4;
}
else
{
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8;
heightBlocks = height / 4;
bpp = 2;
}
// Clamp to minimum number of blocks
if (widthBlocks < 2)
widthBlocks = 2;
if (heightBlocks < 2)
heightBlocks = 2;
dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
[imageData addObject:[NSData dataWithBytes:bytes+dataOffset length:dataSize]];
dataOffset += dataSize;
width = MAX(width >> 1, 1);
height = MAX(height >> 1, 1);
}
success = TRUE;
}
return success;
}
@end

View File

@ -32,7 +32,7 @@
for (int i=0;i<[mipmaps count];i++) {
bitmap = [mipmaps objectAtIndex:i];
[texture setDataToLevel:i sourceRectangle:nil from:(void*)[[bitmap getPixelData] bytes]];
[texture setDataToLevel:i sourceRectangle:nil from:(void*)[[bitmap getPixelData] bytes] ];
}
return texture;

View File

@ -19,6 +19,7 @@
#import "IndexBuffer+Internal.h"
#import "VertexBuffer+Internal.h"
#import "RenderTarget2D+Internal.h"
#import "VectorConverter.h"
@interface GraphicsDevice(){
BOOL rrt;
@ -363,7 +364,7 @@
glDeleteTextures(1, &textureId);
}
- (void) setData:(void*)data toTexture2D:(Texture2D*)texture SourceRectangle:(Rectangle*)rect level:(int)level {
- (void) setData:(void*)data toTexture2D:(Texture2D*)texture SourceRectangle:(Rectangle*)rect level:(int)level{
GLenum format, type;
[GraphicsDevice getFormat:&format AndType:&type ForSurfaceFormat:texture.format];
@ -374,12 +375,30 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (rect) {
glTexSubImage2D(GL_TEXTURE_2D, level, rect.x, rect.y, rect.width, rect.height, format, type, data);
} else {
glTexImage2D(GL_TEXTURE_2D, level, format, texture.width, texture.height, 0, format, type, data);
if (texture.format == SurfaceFormatPvrtc4bAlpha || texture.format == SurfaceFormatPvrtc4b || texture.format == SurfaceFormatPvrtc2bAlpha || texture.format == SurfaceFormatPvrtc2b) {
float bytesPerPixel = 0;
BOOL hasValue = [VectorConverter tryGetSizeInBytesOfSurfaceFormat:texture.format sizeInBytes:&bytesPerPixel];
if (hasValue) {
int size = (int)(bytesPerPixel*texture.width*texture.height);
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, texture.width, texture.height, 0, size, data);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
NSLog(@"Error uploading compressed texture level: %d. glError: 0x%04X", level, err);
}
}else{
[NSException raise:@"ArgumentException" format:@"The provided format is not supported"];
}
}else{
if (rect) {
glTexSubImage2D(GL_TEXTURE_2D, level, rect.x, rect.y, rect.width, rect.height, format, type, data);
} else {
glTexImage2D(GL_TEXTURE_2D, level, format, texture.width, texture.height, 0, format, type, data);
}
}
glBindTexture(GL_TEXTURE_2D, 0);
}
@ -480,6 +499,18 @@
*format = GL_RGBA;
*type = GL_UNSIGNED_SHORT_5_5_5_1;
break;
case SurfaceFormatPvrtc4bAlpha:
*format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
break;
case SurfaceFormatPvrtc4b:
*format = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
break;
case SurfaceFormatPvrtc2bAlpha:
*format = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
break;
case SurfaceFormatPvrtc2b:
*format = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
break;
default:
break;
}

View File

@ -187,7 +187,11 @@ typedef enum {
//SurfaceFormatHalfSingle,
//SurfaceFormatHalfVector2,
//SurfaceFormatHalfVector4,
//SurfaceFormatHdrBlendable
//SurfaceFormatHdrBlendable
SurfaceFormatPvrtc4b,
SurfaceFormatPvrtc2b,
SurfaceFormatPvrtc4bAlpha,
SurfaceFormatPvrtc2bAlpha
} SurfaceFormat;
typedef enum {