RSS

(root)/iphone/common : /source/PVRTexture.m (revision 100)

To get this branch, use:
bzr branch /browse/iphone/common
Line Revision Contents
1 78
/*
2
3
    File: PVRTexture.m
4
Abstract: The PVRTexture class is responsible for loading .pvr files.
5
 Version: 1.3
6
7
Copyright (C) 2010 Apple Inc. All Rights Reserved.
8
9
10
*/
11
12
#import "PVRTexture.h"
13
14
#define PVR_TEXTURE_FLAG_TYPE_MASK	0xff
15
16
static unsigned char gPVRTexIdentifier[4] = "PVR!";
17
18
enum
19
{
20
	kPVRTextureFlagTypePVRTC_2 = 24,
21
	kPVRTextureFlagTypePVRTC_4
22
};
23
24
typedef struct _PVRTexHeader
25
{
26
	uint32_t headerLength;
27
	uint32_t height;
28
	uint32_t width;
29
	uint32_t numMipmaps;
30
	uint32_t flags;
31
	uint32_t dataLength;
32
	uint32_t bpp;
33
	uint32_t bitmaskRed;
34
	uint32_t bitmaskGreen;
35
	uint32_t bitmaskBlue;
36
	uint32_t bitmaskAlpha;
37
	uint32_t pvrTag;
38
	uint32_t numSurfs;
39
} PVRTexHeader;
40
41
42
@implementation PVRTexture
43
/*
44
@synthesize name = _name;
45
@synthesize width = _width;
46
@synthesize height = _height;
47
@synthesize internalFormat = _internalFormat;
48
@synthesize hasAlpha = _hasAlpha;
49
*/
50
51
- (BOOL)unpackPVRData:(NSData *)data
52
{
53
	BOOL success = FALSE;
54
	PVRTexHeader *header = NULL;
55
	uint32_t flags, pvrTag;
56
	uint32_t dataLength = 0, dataOffset = 0, dataSize = 0;
57
	uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
58
	uint32_t width = 0, height = 0, bpp = 4;
59
	uint8_t *bytes = NULL;
60
	uint32_t formatFlags;
61
62
	header = (PVRTexHeader *)[data bytes];
63
64
	pvrTag = CFSwapInt32LittleToHost(header->pvrTag);
65
66
	if (gPVRTexIdentifier[0] != ((pvrTag >>  0) & 0xff) ||
67
		gPVRTexIdentifier[1] != ((pvrTag >>  8) & 0xff) ||
68
		gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) ||
69
		gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff))
70
	{
71
		return FALSE;
72
	}
73
	
74
	flags = CFSwapInt32LittleToHost(header->flags);
75
	formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;
76
	
77
	if (formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2)
78
	{
79
		[_imageData removeAllObjects];
80
		
81
		if (formatFlags == kPVRTextureFlagTypePVRTC_4)
82
			_internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
83
		else if (formatFlags == kPVRTextureFlagTypePVRTC_2)
84
			_internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
85
	
86
		_width = width = CFSwapInt32LittleToHost(header->width);
87
		_height = height = CFSwapInt32LittleToHost(header->height);
88
		
89
		if (CFSwapInt32LittleToHost(header->bitmaskAlpha))
90
			_hasAlpha = TRUE;
91
		else
92
			_hasAlpha = FALSE;
93
		
94
		dataLength = CFSwapInt32LittleToHost(header->dataLength);
95
		
96
		bytes = ((uint8_t *)[data bytes]) + sizeof(PVRTexHeader);
97
		
98
		// Calculate the data size for each texture level and respect the minimum number of blocks
99
		while (dataOffset < dataLength)
100
		{
101
			if (formatFlags == kPVRTextureFlagTypePVRTC_4)
102
			{
103
				blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
104
				widthBlocks = width / 4;
105
				heightBlocks = height / 4;
106
				bpp = 4;
107
			}
108
			else
109
			{
110
				blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
111
				widthBlocks = width / 8;
112
				heightBlocks = height / 4;
113
				bpp = 2;
114
			}
115
			
116
			// Clamp to minimum number of blocks
117
			if (widthBlocks < 2)
118
				widthBlocks = 2;
119
			if (heightBlocks < 2)
120
				heightBlocks = 2;
121
122
			dataSize = widthBlocks * heightBlocks * ((blockSize  * bpp) / 8);
123
			
124
			[_imageData addObject:[NSData dataWithBytes:bytes+dataOffset length:dataSize]];
125
			
126
			dataOffset += dataSize;
127
			
128
			width = MAX(width >> 1, 1U);
129
			height = MAX(height >> 1, 1U);
130
		}
131
				  
132
		success = TRUE;
133
	}
134
	
135
	return success;
136
}
137
138
139
140
- (BOOL)uploadGLTexture
141
{
142
	int width = _width;
143
	int height = _height;
144
	NSData *data;
145
	GLenum err;
146
	
147
	
148
	if ([_imageData count] > 1)
149 86
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
150 78
	else
151
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
152 85
153
	assert(GL_NO_ERROR == glGetError());
154 78
	
155
	for (size_t i=0; i < [_imageData count]; i++)
156
	{
157
		data = [_imageData objectAtIndex:i];
158
		glCompressedTexImage2D(GL_TEXTURE_2D, i, _internalFormat, width, height, 0, [data length], [data bytes]);
159
		
160
		err = glGetError();
161
		if (err != GL_NO_ERROR)
162
		{
163 83
			NSLog(@"Error uploading compressed texture level: %zd. glError: 0x%04X", i, err);
164 78
			return FALSE;
165
		}
166
		
167
		width = MAX(width >> 1, 1);
168
		height = MAX(height >> 1, 1);
169
	}
170
171
	[_imageData removeAllObjects];
172
	
173
	return TRUE;
174
}
175
176
177
- (id)initWithContentsOfFile:(NSString *)path
178
{
179
	if ((self = [super init]))
180
	{
181
		NSData *data = [NSData dataWithContentsOfFile:path];
182
		
183
		_imageData = [[NSMutableArray alloc] initWithCapacity:10];
184
		
185
		_width = _height = 0;
186
		_internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
187
		_hasAlpha = FALSE;
188
		
189
		if (!data || ![self unpackPVRData:data])
190
		{
191
			self = nil;
192
		}
193
	}
194
	
195
	return self;
196
}
197
198
199
+ (BOOL) loadPvrTextureWithContentsOfFile: (NSString *)path intoTexture: (GLuint) name
200
{
201
	PVRTexture* obj = [[PVRTexture alloc] initWithContentsOfFile: path];
202 96.1.1
	
203
	assert(obj);
204
	
205 78
	if (!obj)
206
		return NO;
207
	
208
	
209
	glBindTexture(GL_TEXTURE_2D, name);
210
	
211 85
	assert(GL_NO_ERROR == glGetError());
212
	
213 78
	BOOL ok = [obj uploadGLTexture];
214
	
215
	
216
217
	glBindTexture(GL_TEXTURE_2D, 0);
218 93
	assert(GL_NO_ERROR == glGetError());
219 78
	
220
	return ok;
221
}
222
223
- (void)dealloc
224
{
225
}
226
227
@end

Loggerhead 1.17 is a web-based interface for Bazaar branches