/
Tupac.mm
338 lines (268 loc) · 12.3 KB
/
Tupac.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
//
// Tupac.m
// tupac
//
// Created by Mark Onyschuk on 11-09-09.
// Copyright 2011 Zynga Toronto, Inc. All rights reserved.
//
#import "Tupac.h"
#import "TexturePacker.h"
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
#import "pvrtc.h"
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 Tupac {
TEXTURE_PACKER::TexturePacker* tp; // we hide this ivar in the implementation - requires LLVM Compiler 2.x
}
@synthesize scale=scale_, border=border_, filenames=filenames_, outputName=outputName_, outputFormat=outputFormat_, imageFormat=imageFormat_;
+ (Tupac*) tupac
{
return [[[Tupac alloc] init] autorelease];
}
- (id)init {
if ((self = [super init])) {
scale_ = 1.0;
border_ = NO;
imageFormat_ = kTupacImageFormatPNG;
tp = TEXTURE_PACKER::createTexturePacker();
}
return self;
}
- (void)dealloc {
TEXTURE_PACKER::releaseTexturePacker(tp);
[filenames_ release];
[outputName_ release];
[outputFormat_ release];
[super dealloc];
}
- (NSImage *)rotateImage:(NSImage *)image clockwise:(BOOL)clockwise {
NSImage *existingImage = image;
NSSize existingSize = [existingImage size];
NSSize rotatedSize = NSMakeSize(existingSize.height, existingSize.width);
NSImage *rotatedImage = [[NSImage alloc] initWithSize:rotatedSize];
[rotatedImage lockFocus];
{
/**
* Apply the following transformations:
*
* - bring the rotation point to the centre of the image instead of
* the default lower, left corner (0,0).
* - rotate it by 90 degrees, either clock or counter clockwise.
* - re-translate the rotated image back down to the lower left corner
* so that it appears in the right place.
*/
NSAffineTransform *rotateTF = [NSAffineTransform transform];
NSPoint centerPoint = NSMakePoint(rotatedSize.width / 2, rotatedSize.height / 2);
[rotateTF translateXBy: centerPoint.x yBy: centerPoint.y];
[rotateTF rotateByDegrees: (clockwise) ? - 90 : 90];
[rotateTF translateXBy: -centerPoint.y yBy: -centerPoint.x];
[rotateTF concat];
NSRect rotatedRect = NSMakeRect(0, 0, rotatedSize.height, rotatedSize.width);
[existingImage drawInRect:rotatedRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
[rotatedImage unlockFocus];
return rotatedImage;
}
- (void)createTextureAtlas {
NSMutableArray *images = [NSMutableArray arrayWithCapacity:self.filenames.count];
for (NSString *filename in self.filenames) {
NSImage *image = [[NSImage alloc] initWithContentsOfFile:filename];
if (image == nil) {
fprintf(stderr, "unable to load image %s\n", [filename UTF8String]);
exit(EXIT_FAILURE);
}
[image setFlipped:YES];
[image setSize:NSMakeSize(image.size.width * self.scale, image.size.height * self.scale)];
[images addObject:image];
[image release];
}
if (![self.outputFormat isEqualToString:TupacOutputFormatCocos2D]
&& ![self.outputFormat isEqualToString:TupacOutputFormatAndEngine]) {
fprintf(stderr, "unknown output format %s\n", [self.outputFormat UTF8String]);
exit(EXIT_FAILURE);
}
tp->setTextureCount((int)[images count]);
for (NSImage *image in images) tp->addTexture((int)image.size.width, (int)image.size.height);
int outW, outH;
if (tp->packTextures(outW, outH, true, self.border) == 0) {
fprintf(stderr, "unable to pack images\n");
exit(EXIT_FAILURE);
}
NSBitmapImageRep *outRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:outW
pixelsHigh:outH
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat:0 //NSAlphaFirstBitmapFormat
bytesPerRow:(32 / 8) * outW
bitsPerPixel:32];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:outRep]];
NSAffineTransform *transform = [NSAffineTransform transform];
[transform scaleXBy:1.0 yBy:-1.0];
[transform translateXBy:0.0 yBy:-outH];
[transform concat];
// draw our individual images
{
int index = 0;
for (NSImage *image in images) {
bool rot;
int x, y, w, h;
rot = tp->getTextureLocation(index++, x, y, w, h);
if (rot == true) image = [self rotateImage:image clockwise:YES];
[image drawInRect:NSMakeRect(x, y, w, h) fromRect:NSZeroRect
operation:NSCompositeSourceOver fraction:1.0 respectFlipped:NO
hints:[NSDictionary dictionaryWithObjectsAndKeys:transform, NSImageHintCTM, nil]];
}
}
[NSGraphicsContext restoreGraphicsState];
NSString* textureFileName = NULL;
if (imageFormat_ == kTupacImageFormatPNG)
{
//
// PNG Export
//
NSString *pngFilename = [self.outputName stringByAppendingPathExtension:@"png"];
[[outRep representationUsingType:NSPNGFileType properties:nil] writeToFile:pngFilename atomically:YES];
textureFileName = pngFilename;
}
else if (imageFormat_ == kTupacImageFormatPVR)
{
//
// PVR Export
//
pvrtc_info_output(stdout);
size_t pvrOutputSize = pvrtc_size((int)outRep.pixelsWide, // width
(int)outRep.pixelsHigh, // height
0, // generate mipmaps
0); // use 2bpp compression
NSMutableData *pvrData = [[NSMutableData alloc] initWithLength:pvrOutputSize];
NSString *pvrFilename = [self.outputName stringByAppendingPathExtension:@"pvr"];
if (outW == outH) {
// if square, we use PVRC (compressed) format for our data
pvrtc_compress([outRep bitmapData], // input data
[pvrData mutableBytes], // output data
(int)outRep.pixelsWide, // resize width
(int)outRep.pixelsHigh, // resize height
0, // generate mipmaps
1, // alpha on
0, // texture wraps
0); // use 2bpp compression
}
else {
// if not square, we construct a file with a simple header followed by uncompressed data
PVRTexHeader header;
header.headerLength = sizeof(PVRTexHeader);
header.width = (uint32_t)outRep.pixelsWide;
header.height = (uint32_t)outRep.pixelsHigh;
header.numMipmaps = 0;
header.bpp = 32;
header.flags = 32786;
header.dataLength = (uint32_t)(outRep.pixelsWide * outRep.pixelsHigh * 4);
header.bitmaskRed = 0xFF000000;
header.bitmaskBlue = 0x0000FF00;
header.bitmaskGreen = 0x00FF0000;
header.bitmaskAlpha = 0x000000FF;
header.pvrTag = 559044176;
header.numSurfs = 1;
[pvrData setLength:0];
[pvrData appendBytes:&header length:sizeof(PVRTexHeader)];
[pvrData appendBytes:[outRep bitmapData] length:outRep.pixelsWide * outRep.pixelsHigh * 4];
}
[pvrData writeToFile:pvrFilename atomically:YES];
[pvrData release];
[outRep release];
textureFileName = pvrFilename;
}
//
// Metadata File Export
//
if ([self.outputFormat isEqualToString:TupacOutputFormatCocos2D]) {
NSMutableDictionary *outDict = [[NSMutableDictionary alloc] initWithCapacity:2];
NSMutableDictionary *frames = [NSMutableDictionary dictionaryWithCapacity:self.filenames.count];
NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithCapacity:4];
[outDict setObject:frames forKey:@"frames"];
[outDict setObject:metadata forKey:@"metadata"];
int index = 0;
for (NSString *filename in self.filenames) {
bool rot;
int x, y, w, h;
rot = tp->getTextureLocation(index++, x, y, w, h);
[frames setObject:[NSDictionary dictionaryWithObjectsAndKeys:
NSStringFromRect(NSMakeRect(x, y, w, h)), @"frame",
NSStringFromPoint(NSMakePoint(0, 0)), @"offset",
[NSNumber numberWithBool:rot], @"rotated",
NSStringFromRect(NSMakeRect(x, y, w, h)), @"sourceColorRect",
NSStringFromSize(NSMakeSize(w, h)), @"sourceSize",
nil]
forKey:[filename lastPathComponent]];
}
[metadata setObject:textureFileName forKey:@"realTextureFilename"];
[metadata setObject:textureFileName forKey:@"textureFilename"];
[metadata setObject:[NSNumber numberWithInt:2] forKey:@"format"];
[metadata setObject:NSStringFromSize(NSMakeSize(outW, outH)) forKey:@"size"];
[outDict writeToFile:[self.outputName stringByAppendingPathExtension:@"plist"] atomically:YES];
[outDict release];
}
else if ([self.outputFormat isEqualToString:TupacOutputFormatAndEngine]) {
fprintf(stderr, "[MO] output format %s not yet supported\n", [self.outputFormat UTF8String]);
exit(EXIT_FAILURE);
}
}
- (void) createTextureAtlasFromDirectoryPaths:(NSArray *)dirs
{
NSFileManager* fm = [NSFileManager defaultManager];
// Build a list of all file names from all directories
NSMutableSet* allFiles = [NSMutableSet set];
for (NSString* dir in dirs)
{
NSArray* files = [fm contentsOfDirectoryAtPath:dir error:NULL];
for (NSString* file in files)
{
if ([[[file pathExtension] lowercaseString] isEqualToString:@"png"])
{
[allFiles addObject:[file lastPathComponent]];
}
}
}
// Add all the absolute file names to an array from the correct directories
NSMutableArray* absoluteFilepaths = [NSMutableArray array];
for (NSString* file in allFiles)
{
BOOL foundFile = NO;
for (NSString* dir in dirs)
{
NSString* absFilepath = [dir stringByAppendingPathComponent:file];
if ([fm fileExistsAtPath:absFilepath])
{
[absoluteFilepaths addObject:absFilepath];
//foundFile = YES;
break;
}
}
}
// Generate the sprite sheet
self.filenames = absoluteFilepaths;
[self createTextureAtlas];
}
@end
NSString *TupacOutputFormatCocos2D = @"cocos2d";
NSString *TupacOutputFormatAndEngine = @"andengine";