Created
August 28, 2013 09:26
-
-
Save Shilo/6364072 to your computer and use it in GitHub Desktop.
An extension for Sparrow that allows 9-slice scaling of images and buttons.
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
// | |
// XDImage.h | |
// XDImage | |
// | |
// Created by Shilo White on 8/26/13. | |
// | |
// | |
#import "SPDisplayObjectContainer.h" | |
#import "SPButton.h" | |
#import "SPRectangle.h" | |
@class SPTexture; | |
@interface XDImage : SPDisplayObjectContainer | |
- (id)initWithContentsOfFile:(NSString *)path; | |
- (id)initWithContentsOfFile:(NSString *)path generateMipmaps:(BOOL)mipmaps; | |
- (id)initWithTexture:(SPTexture *)texture; | |
- (id)initWithWidth:(float)width height:(float)height; | |
- (id)initWithWidth:(float)width height:(float)height color:(uint)color; | |
- (id)initWithWidth:(float)width height:(float)height color:(uint)color premultipliedAlpha:(BOOL)pma; | |
+ (id)imageWithContentsOfFile:(NSString *)path; | |
+ (id)imageWithContentsOfFile:(NSString *)path generateMipmaps:(BOOL)mipmaps; | |
+ (id)imageWithTexture:(SPTexture *)texture; | |
+ (id)image; | |
+ (id)imageWithWidth:(float)width height:(float)height; | |
+ (id)imageWithWidth:(float)width height:(float)height color:(uint)color; | |
+ (id)imageWithWidth:(float)width height:(float)height color:(uint)color premultipliedAlpha:(BOOL)pma; | |
@property (nonatomic, strong) SPTexture *texture; | |
@property (nonatomic, copy) SPRectangle *scale9Grid; | |
@property (nonatomic, assign) BOOL debugDraw; | |
@property (nonatomic, assign) uint color; | |
@property (nonatomic, readonly) BOOL tinted; | |
@property (nonatomic, assign) BOOL premultipliedAlpha; | |
@end | |
@interface XDButton : SPButton | |
@property (nonatomic, copy) SPRectangle *scale9Grid; | |
@property (nonatomic, assign) BOOL debugDraw; | |
@end | |
@interface SPRectangle (XDImage) | |
+ (SPRectangle *)rectangleCenteredToBounds:(SPRectangle *)bounds; | |
+ (SPRectangle *)rectangleCenteredToBounds:(SPRectangle *)bounds width:(float)width height:(float)height; | |
@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
// | |
// XDImage.m | |
// XDImage | |
// | |
// Created by Shilo White on 8/26/13. | |
// | |
// | |
#import "XDImage.h" | |
#import "SPTexture.h" | |
#import "SPImage.h" | |
#import "SPQuad.h" | |
#import "SPDisplayObject.h" | |
@implementation XDImage | |
{ | |
SPTexture *_texture; | |
SPRectangle *_scale9Grid; | |
BOOL _debugDraw; | |
SPQuad *_debugDrawQuad; | |
SPImage *_images[9]; | |
float _originalPivotX; | |
float _originalPivotY; | |
BOOL _tinted; | |
} | |
@synthesize texture = _texture; | |
@synthesize scale9Grid = _scale9Grid; | |
@synthesize debugDraw = _debugDraw; | |
- (id)initWithContentsOfFile:(NSString *)path | |
{ | |
return [self initWithContentsOfFile:path generateMipmaps:NO]; | |
} | |
- (id)initWithContentsOfFile:(NSString *)path generateMipmaps:(BOOL)mipmaps | |
{ | |
return [self initWithTexture:[SPTexture textureWithContentsOfFile:path generateMipmaps:mipmaps]]; | |
} | |
- (id)initWithTexture:(SPTexture *)texture | |
{ | |
if (!texture) [NSException raise:SP_EXC_INVALID_OPERATION format:@"texture cannot be nil!"]; | |
if ((self = [super init])) | |
{ | |
_texture = texture; | |
_images[0] = [[SPImage alloc] initWithTexture:_texture]; | |
[self addChild:_images[0]]; | |
} | |
return self; | |
} | |
- (id)init | |
{ | |
return [self initWithWidth:32 height:32]; | |
} | |
- (id)initWithWidth:(float)width height:(float)height | |
{ | |
return [self initWithTexture:[SPTexture textureWithWidth:width height:height draw:NULL]]; | |
} | |
- (id)initWithWidth:(float)width height:(float)height color:(uint)color | |
{ | |
return [self initWithWidth:width height:height color:color premultipliedAlpha:YES]; | |
} | |
- (id)initWithWidth:(float)width height:(float)height color:(uint)color premultipliedAlpha:(BOOL)pma | |
{ | |
if ((self = [super init])) | |
{ | |
_texture = nil; | |
_images[0] = [[SPImage alloc] initWithTexture:_texture]; | |
[self addChild:_images[0]]; | |
} | |
return self; | |
} | |
- (void)recreateImages | |
{ | |
if (_scale9Grid) | |
{ | |
for (int i=1; i<9; i++) | |
{ | |
[_images[i] removeFromParent]; | |
_images[i] = nil; | |
SPRectangle *region = [self textureRegionForIndex:i]; | |
if (region.width && region.height) | |
{ | |
SPTexture *texture = [SPTexture textureWithRegion:region ofTexture:_texture]; | |
_images[i] = [[SPImage alloc] initWithTexture:texture]; | |
_images[i].x = region.x; | |
_images[i].y = region.y; | |
[self addChild:_images[i]]; | |
} | |
} | |
_images[0].texture = [SPTexture textureWithRegion:_scale9Grid ofTexture:_texture]; | |
_images[0].x = _scale9Grid.x; | |
_images[0].y = _scale9Grid.y; | |
[_images[0] readjustSize]; | |
} | |
else | |
{ | |
for (int i=8; i>0; i--) | |
{ | |
[_images[i] removeFromParent]; | |
_images[i] = nil; | |
} | |
_images[0].texture = _texture; | |
_images[0].x = _images[0].y = 0; | |
[_images[0] readjustSize]; | |
} | |
} | |
- (SPRectangle *)textureRegionForIndex:(uint)index | |
{ | |
float x, y, width, height; | |
x = y = width = height = 0.0f; | |
switch (index) { | |
case 1: | |
width = _scale9Grid.x; | |
height = _scale9Grid.y; | |
break; | |
case 2: | |
x = _scale9Grid.x; | |
width = _scale9Grid.width; | |
height = _scale9Grid.y; | |
break; | |
case 3: | |
x = _scale9Grid.x+_scale9Grid.width; | |
width = _texture.width-x; | |
height = _scale9Grid.y; | |
break; | |
case 4: | |
y = _scale9Grid.y; | |
width = _scale9Grid.x; | |
height = _scale9Grid.height; | |
break; | |
case 5: | |
x = _scale9Grid.x+_scale9Grid.width; | |
y = _scale9Grid.y; | |
width = _texture.width-x; | |
height = _scale9Grid.height; | |
break; | |
case 6: | |
y = _scale9Grid.y+_scale9Grid.height; | |
width = _scale9Grid.x; | |
height = _texture.height-y; | |
break; | |
case 7: | |
x = _scale9Grid.x; | |
y = _scale9Grid.y+_scale9Grid.height; | |
width = _scale9Grid.width; | |
height = _texture.height-y; | |
break; | |
case 8: | |
x = _scale9Grid.x+_scale9Grid.width; | |
y = _scale9Grid.y+_scale9Grid.height; | |
width = _texture.width-x; | |
height = _texture.height-y; | |
break; | |
} | |
return [SPRectangle rectangleWithX:x y:y width:width height:height]; | |
} | |
- (void)setTexture:(SPTexture *)texture | |
{ | |
if (texture == nil) | |
{ | |
[NSException raise:SP_EXC_INVALID_OPERATION format:@"texture cannot be nil!"]; | |
} | |
else if (texture != _texture) | |
{ | |
_texture = texture;; | |
[self recreateImages]; | |
} | |
} | |
- (void)setScale9Grid:(SPRectangle *)scale9Grid | |
{ | |
BOOL shouldRecreateImages = ((_scale9Grid != nil) || (scale9Grid != nil)); | |
_scale9Grid = [scale9Grid copy]; | |
if (shouldRecreateImages) [self recreateImages]; | |
if (_debugDraw) [self alignDebugDrawQuad]; | |
} | |
- (void)setDebugDraw:(BOOL)debugDraw | |
{ | |
if (debugDraw != _debugDraw) | |
{ | |
_debugDraw = debugDraw; | |
if (_debugDraw) | |
{ | |
_debugDrawQuad = [SPQuad quad]; | |
_debugDrawQuad.color = 0x00ff00; | |
_debugDrawQuad.alpha = 0.5f; | |
[self alignDebugDrawQuad]; | |
[self addChild:_debugDrawQuad]; | |
} | |
else | |
{ | |
[_debugDrawQuad removeFromParent]; | |
_debugDrawQuad = nil; | |
} | |
} | |
} | |
- (void)alignDebugDrawQuad | |
{ | |
if (_scale9Grid) | |
{ | |
_debugDrawQuad.x = _scale9Grid.x; | |
_debugDrawQuad.y = _scale9Grid.y; | |
_debugDrawQuad.width = _scale9Grid.width; | |
_debugDrawQuad.height = _scale9Grid.height; | |
} | |
_debugDrawQuad.visible = (BOOL)_scale9Grid; | |
} | |
- (void)setScaleX:(float)scaleX | |
{ | |
if (_scale9Grid) | |
{ | |
float targetWidth = _texture.width * scaleX; | |
float middleTargetWidth = targetWidth - _images[4].texture.width - _images[5].texture.width; | |
float middleScaleX = 0.0f; | |
float sideScaleX = 1.0f; | |
if(middleTargetWidth < 0.0f) | |
sideScaleX = scaleX*(_texture.width/(_images[4].texture.width+_images[5].texture.width)); | |
else | |
middleScaleX = middleTargetWidth / _images[0].texture.width; | |
//left | |
_images[1].scaleX = _images[4].scaleX = _images[6].scaleX = sideScaleX; | |
//middle | |
_images[0].x = _images[2].x = _images[7].x = _images[1].width; | |
_images[0].scaleX = _images[2].scaleX = _images[7].scaleX = middleScaleX; | |
//right | |
_images[3].x = _images[5].x = _images[8].x = _images[0].x + _images[0].width; | |
_images[3].scaleX = _images[5].scaleX = _images[8].scaleX = sideScaleX; | |
if (_debugDraw) | |
{ | |
_debugDrawQuad.x = _images[0].x; | |
_debugDrawQuad.width = _images[0].width; | |
} | |
} | |
else | |
_images[0].scaleX = scaleX; | |
if (_originalPivotX) | |
[super setPivotX:self.width*(_originalPivotX/_texture.width)]; | |
} | |
- (void)setScaleY:(float)scaleY | |
{ | |
if (_scale9Grid) | |
{ | |
float targetHeight = _texture.height * scaleY; | |
float middleTargetHeight = targetHeight - _images[2].texture.height - _images[7].texture.height; | |
float middleScaleY = 0.0f; | |
float sideScaleY = 1.0f; | |
if(middleTargetHeight < 0.0f) | |
sideScaleY = scaleY*(_texture.height/(_images[2].texture.height+_images[7].texture.height)); | |
else | |
middleScaleY = middleTargetHeight / _images[0].texture.height; | |
//top | |
_images[1].scaleY = _images[2].scaleY = _images[3].scaleY = sideScaleY; | |
//middle | |
_images[0].y = _images[4].y = _images[5].y = _images[1].height; | |
_images[0].scaleY = _images[4].scaleY = _images[5].scaleY = middleScaleY; | |
//bottom | |
_images[6].y = _images[7].y = _images[8].y = _images[0].y + _images[0].height; | |
_images[6].scaleY = _images[7].scaleY = _images[8].scaleY = sideScaleY; | |
if (_debugDraw) | |
{ | |
_debugDrawQuad.y = _images[0].y; | |
_debugDrawQuad.height = _images[0].height; | |
} | |
} | |
else | |
_images[0].scaleY = scaleY; | |
if (_originalPivotY) | |
[super setPivotY:self.height*(_originalPivotY/_texture.height)]; | |
} | |
- (void)setPivotX:(float)pivotX | |
{ | |
if (pivotX != _originalPivotX) | |
{ | |
_originalPivotX = pivotX; | |
[super setPivotX:self.width*(pivotX/_texture.width)]; | |
} | |
} | |
- (float)pivotX | |
{ | |
return _originalPivotX; | |
} | |
- (void)setPivotY:(float)pivotY | |
{ | |
if (pivotY != _originalPivotY) | |
{ | |
_originalPivotY = pivotY; | |
[super setPivotY:self.height*(pivotY/_texture.height)]; | |
} | |
} | |
- (float)pivotY | |
{ | |
return _originalPivotY; | |
} | |
- (void)setColor:(uint)color | |
{ | |
for (int i=0; i<9; i++) | |
_images[i].color = color; | |
if (color != 0xffffff) _tinted = YES; | |
else _tinted = (self.alpha != 1.0f) || _images[0].tinted; | |
} | |
- (uint)color | |
{ | |
return _images[0].color; | |
} | |
- (void)setAlpha:(float)alpha | |
{ | |
super.alpha = alpha; | |
if (self.alpha != 1.0f) _tinted = true; | |
else _tinted = _images[0].tinted; | |
} | |
- (BOOL)tinted | |
{ | |
return _tinted; | |
} | |
- (void)setPremultipliedAlpha:(BOOL)premultipliedAlpha | |
{ | |
for (int i=0; i<9; i++) | |
_images[i].premultipliedAlpha = premultipliedAlpha; | |
} | |
- (BOOL)premultipliedAlpha | |
{ | |
return _images[0].premultipliedAlpha; | |
} | |
- (void)addChild:(SPDisplayObject *)child | |
{ | |
[super addChild:child]; | |
if (_debugDraw) [super addChild:_debugDrawQuad]; | |
} | |
+ (id)imageWithContentsOfFile:(NSString *)path | |
{ | |
return [[self alloc] initWithContentsOfFile:path]; | |
} | |
+ (id)imageWithContentsOfFile:(NSString *)path generateMipmaps:(BOOL)mipmaps | |
{ | |
return [[self alloc] initWithContentsOfFile:path generateMipmaps:mipmaps]; | |
} | |
+ (id)imageWithTexture:(SPTexture *)texture | |
{ | |
return [[self alloc] initWithTexture:texture]; | |
} | |
+ (id)image | |
{ | |
return [[self alloc] init]; | |
} | |
+ (id)imageWithWidth:(float)width height:(float)height | |
{ | |
return [[self alloc] initWithWidth:width height:height]; | |
} | |
+ (id)imageWithWidth:(float)width height:(float)height color:(uint)color | |
{ | |
return [[self alloc] initWithWidth:width height:height color:color]; | |
} | |
+ (id)imageWithWidth:(float)width height:(float)height color:(uint)color premultipliedAlpha:(BOOL)pma | |
{ | |
return [[self alloc] initWithWidth:width height:height color:color premultipliedAlpha:pma]; | |
} | |
@end | |
@interface SPButton () | |
{ | |
@protected | |
XDImage *_background; | |
SPSprite *_contents; | |
} | |
@end | |
@implementation XDButton | |
{ | |
float _originalPivotX; | |
float _originalPivotY; | |
} | |
- (id)initWithUpState:(SPTexture*)upState downState:(SPTexture*)downState | |
{ | |
if ((self = [super initWithUpState:upState downState:downState])) | |
{ | |
[_background removeFromParent]; | |
_background = [XDImage imageWithTexture:upState]; | |
[_contents addChild:_background atIndex:0]; | |
} | |
return self; | |
} | |
- (void)setScale9Grid:(SPRectangle *)scale9Grid | |
{ | |
[_background setScale9Grid:scale9Grid]; | |
} | |
- (SPRectangle *)scale9Grid | |
{ | |
return [_background scale9Grid]; | |
} | |
- (void)setDebugDraw:(BOOL)debugDraw | |
{ | |
[_background setDebugDraw:debugDraw]; | |
} | |
- (BOOL)debugDraw | |
{ | |
return [_background debugDraw]; | |
} | |
- (void)setWidth:(float)width | |
{ | |
[super setWidth:width]; | |
if (_originalPivotX) | |
[super setPivotX:self.width*(_originalPivotX/_background.texture.width)]; | |
} | |
- (void)setHeight:(float)height | |
{ | |
[super setHeight:height]; | |
if (_originalPivotY) | |
[super setPivotY:self.height*(_originalPivotY/_background.texture.height)]; | |
} | |
- (void)setScaleX:(float)scaleX | |
{ | |
[_background setScaleX:scaleX]; | |
if (_originalPivotX) | |
[super setPivotX:self.width*(_originalPivotX/_background.texture.width)]; | |
} | |
- (float)scaleX | |
{ | |
return [_background scaleX]; | |
} | |
- (void)setScaleY:(float)scaleY | |
{ | |
[_background setScaleY:scaleY]; | |
if (_originalPivotY) | |
[super setPivotY:self.height*(_originalPivotY/_background.texture.height)]; | |
} | |
- (float)scaleY | |
{ | |
return [_background scaleY]; | |
} | |
- (void)setPivotX:(float)pivotX | |
{ | |
if (pivotX != _originalPivotX) | |
{ | |
_originalPivotX = pivotX; | |
[super setPivotX:self.width*(pivotX/_background.texture.width)]; | |
} | |
} | |
- (float)pivotX | |
{ | |
return _originalPivotX; | |
} | |
- (void)setPivotY:(float)pivotY | |
{ | |
if (pivotY != _originalPivotY) | |
{ | |
_originalPivotY = pivotY; | |
[super setPivotY:self.height*(pivotY/_background.texture.height)]; | |
} | |
} | |
- (float)pivotY | |
{ | |
return _originalPivotY; | |
} | |
@end | |
@implementation SPRectangle (XDImage) | |
+ (SPRectangle *)rectangleCenteredToBounds:(SPRectangle *)bounds | |
{ | |
return [self rectangleCenteredToBounds:bounds width:1.0f height:1.0f]; | |
} | |
+ (SPRectangle *)rectangleCenteredToBounds:(SPRectangle *)bounds width:(float)width height:(float)height | |
{ | |
return [SPRectangle rectangleWithX:(int)((bounds.width-width)/2.0f) y:(int)((bounds.height-height)/2.0f) width:width height:height]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment