Skip to content

Instantly share code, notes, and snippets.

@Shilo
Created August 28, 2013 09:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Shilo/6364072 to your computer and use it in GitHub Desktop.
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.
//
// 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
//
// 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