Created
August 11, 2012 04:56
-
-
Save mohiji/3321199 to your computer and use it in GitHub Desktop.
My texture atlas tool and sample code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# Compiles texture atlases exported from TexturePacker (in the Generic XML format) | |
# into a binary format for fast/easy loading in game code. At the moment this doesn't | |
# support trimming or rotation, 'cause I don't really need 'em. | |
# | |
# The file format (all multibyte values are little endian): | |
# struct AtlasHeader { | |
# uint32_t magic; // (1635019891, or 'atls') | |
# uint16_t textureWidth; | |
# uint16_t textureHeight; | |
# uint16_t numFrames; | |
# char textureName[64]; | |
# }; | |
# sizeof(struct AtlasHeader) == 74; | |
# | |
# struct AtlasFrame { | |
# char name[64]; | |
# uint16_t width, height; | |
# float u1, v1; | |
# float u2, v2; | |
# }; | |
# sizeof(struct AtlasFrame) == 84; | |
import struct | |
import xml.sax | |
from optparse import OptionParser | |
class Texture: | |
def __init__(self, xml_attrs): | |
self.name = xml_attrs.getValue('imagePath') | |
self.width = int(xml_attrs.getValue('width')) | |
self.height = int(xml_attrs.getValue('height')) | |
class Frame: | |
def __init__(self, xml_attrs): | |
self.name = xml_attrs.getValue('n') | |
self.x = float(xml_attrs.getValue('x')) | |
self.y = float(xml_attrs.getValue('y')) | |
self.width = float(xml_attrs.getValue('w')) | |
self.height = float(xml_attrs.getValue('h')) | |
self.u1 = 0 | |
self.v1 = 0 | |
self.u2 = 0 | |
self.v2 = 0 | |
def make_bytes(self): | |
""" | |
Returns the byte-array representation of the frame. | |
""" | |
frame_fmt = '<64s2H4f' | |
return struct.pack(frame_fmt, str(self.name), int(self.width), int(self.height), self.u1, self.v1, self.u2, self.v2) | |
class AtlasHandler(xml.sax.ContentHandler): | |
def __init__(self): | |
xml.sax.ContentHandler.__init__(self) | |
self.texture = None | |
self.frames = [] | |
def startElement(self, name, attrs): | |
if name == 'TextureAtlas': | |
self.texture = Texture(attrs) | |
elif name == 'sprite': | |
self.frames.append(Frame(attrs)) | |
def endElement(self, name): | |
if name == 'TextureAtlas': | |
for frame in self.frames: | |
frame.u1 = frame.x / self.texture.width | |
frame.v1 = frame.y / self.texture.height | |
frame.u2 = (frame.x + frame.width) / self.texture.width | |
frame.v2 = (frame.y + frame.height) / self.texture.height | |
def dumpDescription(self): | |
if self.texture: | |
print("Texture: %s (%s, %s)" % (self.texture.name, self.texture.width, self.texture.height)) | |
else: | |
print("No texture!") | |
for frame in self.frames: | |
print(" Frame: %s" % frame.name) | |
print(" x: %s" % frame.x) | |
print(" y: %s" % frame.y) | |
print(" width: %s" % frame.width) | |
print(" height: %s" % frame.height) | |
print(" u1: %s" % frame.u1) | |
print(" v1: %s" % frame.v1) | |
print(" u2: %s" % frame.u2) | |
print(" v2: %s" % frame.v2) | |
def make_header(self): | |
""" | |
Returns a byte array representation of the binary file header. | |
""" | |
header_fmt = "<ccccHHH64s" | |
return struct.pack(header_fmt, 'a', 't', 'l', 's', self.texture.width, self.texture.height, len(self.frames), str(self.texture.name)) | |
def write_file(self, f): | |
""" | |
Writes out the byte array representation of the texture atlas to the given file. | |
""" | |
f.write(self.make_header()) | |
for frame in self.frames: | |
f.write(frame.make_bytes()) | |
def parse_file(filename): | |
source = open(filename) | |
handler = AtlasHandler() | |
xml.sax.parse(source, handler) | |
return handler | |
USAGE=""" | |
Usage: atlasc.py -o <output-filename> <input-filename> | |
""" | |
def handle_commandline(): | |
""" | |
Handles command-line options and arguments and does the right things. | |
""" | |
parser = OptionParser(usage=USAGE) | |
parser.add_option('-o', '--output', dest='output', default='out.atlas') | |
(options, args) = parser.parse_args() | |
if len(args) == 0: | |
print(USAGE) | |
return -1 | |
atlas = parse_file(args[0]) | |
out = open(options.output, 'w') | |
atlas.write_file(out) | |
out.close() | |
return 0 | |
if __name__ == '__main__': | |
return handle_commandline() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// TextureAtlas.h | |
// | |
// Created by Jonathan Fischer on 8/10/12. | |
// Copyright (c) 2012 Jonathan Fischer. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@interface AtlasFrame : NSObject | |
@property (nonatomic, assign) int width; | |
@property (nonatomic, assign) int height; | |
@property (nonatomic, assign) float u1; | |
@property (nonatomic, assign) float v1; | |
@property (nonatomic, assign) float u2; | |
@property (nonatomic, assign) float v2; | |
@end | |
@interface TextureAtlas : NSObject | |
{ | |
NSMutableDictionary *_atlasFrames; | |
} | |
- (id)initWithContentsOfFile:(NSString*)path; | |
- (id)initWithData:(NSData*)data; | |
@property (nonatomic, readonly) int textureWidth; | |
@property (nonatomic, readonly) int textureHeight; | |
@property (nonatomic, readonly) NSString *textureName; | |
- (AtlasFrame*)frameForName:(NSString*)name; | |
- (void)addFrame:(AtlasFrame*)frame withName:(NSString*)name; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// TextureAtlas.m | |
// | |
// Created by Jonathan Fischer on 8/10/12. | |
// Copyright (c) 2012 Jonathan Fischer. All rights reserved. | |
// | |
#import "TextureAtlas.h" | |
#include "Utility.h" | |
static const uint32_t kMagicNumber = 1635019891; // 'atls' | |
static const uint32_t kHeaderOffsetWidth = 4; | |
static const uint32_t kHeaderOffsetHeight = 6; | |
static const uint32_t kHeaderOffsetNumFrames = 8; | |
static const uint32_t kHeaderOffsetTextureName = 10; | |
static const uint32_t kNameSize = 64; | |
static const uint32_t kOffsetFirstFrame = 74; | |
static const uint32_t kFrameSize = 84; | |
static const uint32_t kFrameOffsetWidth = 64; | |
static const uint32_t kFrameOffsetHeight = 66; | |
static const uint32_t kFrameOffsetU1 = 68; | |
static const uint32_t kFrameOffsetV1 = 72; | |
static const uint32_t kFrameOffsetU2 = 76; | |
static const uint32_t kFrameOffsetV2 = 80; | |
struct AtlasHeader { | |
uint32_t magic; // (1635019891, or 'atls') | |
uint16_t textureWidth; | |
uint16_t textureHeight; | |
uint16_t numFrames; | |
char textureName[64]; | |
}; | |
struct AtlasFrame { | |
char name[64]; | |
uint16_t width, height; | |
float u1, v1; | |
float u2, v2; | |
}; | |
static void readHeader(struct AtlasHeader *header, const uint8_t *bytes) | |
{ | |
header->magic = ReadLittleUint32(bytes); | |
header->textureWidth = ReadLittleUint16(bytes + kHeaderOffsetWidth); | |
header->textureHeight = ReadLittleUint16(bytes + kHeaderOffsetHeight); | |
header->numFrames = ReadLittleUint16(bytes + kHeaderOffsetNumFrames); | |
memcpy(header->textureName, bytes + kHeaderOffsetTextureName, kNameSize); | |
} | |
static void readSprite(struct AtlasFrame *frame, const uint8_t *bytes) | |
{ | |
memcpy(frame->name, bytes, kNameSize); | |
frame->width = ReadLittleUint16(bytes + kFrameOffsetWidth); | |
frame->height = ReadLittleUint16(bytes + kFrameOffsetHeight); | |
frame->u1 = ReadLittleFloat(bytes + kFrameOffsetU1); | |
frame->v1 = ReadLittleFloat(bytes + kFrameOffsetV1); | |
frame->u2 = ReadLittleFloat(bytes + kFrameOffsetU2); | |
frame->v2 = ReadLittleFloat(bytes + kFrameOffsetV2); | |
} | |
@implementation AtlasFrame | |
@synthesize width, height, u1, v1, u2, v2; | |
@end | |
@implementation TextureAtlas | |
@synthesize textureWidth = _textureWidth; | |
@synthesize textureHeight = _textureHeight; | |
@synthesize textureName = _textureName; | |
- (id)initWithContentsOfFile:(NSString *)path | |
{ | |
NSData *data = [[NSData alloc] initWithContentsOfFile:path]; | |
self = [self initWithData:data]; | |
return self; | |
} | |
- (id)initWithData:(NSData *)data | |
{ | |
if (data == nil) { | |
return nil; | |
} | |
self = [super init]; | |
if (self) { | |
_atlasFrames = [[NSMutableDictionary alloc] init]; | |
const uint8_t *bytes = [data bytes]; | |
struct AtlasHeader header; | |
readHeader(&header, bytes); | |
_textureWidth = header.textureWidth; | |
_textureHeight = header.textureHeight; | |
_textureName = [[NSString alloc] initWithCString:header.textureName encoding:NSASCIIStringEncoding]; | |
uint32_t offset = kOffsetFirstFrame; | |
for (int i = 0;i < header.numFrames; i++) { | |
struct AtlasFrame readFrame; | |
readSprite(&readFrame, bytes + offset); | |
AtlasFrame *frame = [[AtlasFrame alloc] init]; | |
frame.width = readFrame.width; | |
frame.height = readFrame.height; | |
frame.u1 = readFrame.u1; | |
frame.v1 = readFrame.v1; | |
frame.u2 = readFrame.u2; | |
frame.v2 = readFrame.v2; | |
NSString *name = [NSString stringWithCString:readFrame.name encoding:NSASCIIStringEncoding]; | |
[self addFrame:frame withName:name]; | |
offset += kFrameSize; | |
} | |
} | |
return self; | |
} | |
- (void)addFrame:(AtlasFrame *)frame withName:(NSString *)name | |
{ | |
if (frame == nil || name == nil) { | |
return; | |
} | |
[_atlasFrames setObject:frame forKey:name]; | |
} | |
- (AtlasFrame*)frameForName:(NSString *)name | |
{ | |
return [_atlasFrames objectForKey:name]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment