Skip to content

Instantly share code, notes, and snippets.

@jimkang
Created September 18, 2010 16:40
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 jimkang/585828 to your computer and use it in GitHub Desktop.
Save jimkang/585828 to your computer and use it in GitHub Desktop.
//
// OGColorTools.h
//
// Created by Jim on 1/25/10.
// Copyright (c) 2010 Jim Kang/Phalange Software.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "cocos2d.h"
typedef enum
{
kRedComp = 0,
kGreenComp,
kBlueComp
} ColorComponent;
typedef enum
{
kRedToBlue = 0,
kBlueToRed
} HueShiftDirection;
// This represents the changes that should be applied to a color.
typedef struct _ColorTransform
{
GLbyte r;
GLbyte g;
GLbyte b;
} ColorTransform;
@interface OGColorTools : NSObject
{
}
+ (GLubyte)addColorComponent:(NSInteger)component to:(NSInteger)otherComponent;
+ (ccColor3B)color:(ccColor3B)clr shiftHue:(CGFloat)percent inDirection:(HueShiftDirection)direction;
@end
//
// OGColorTools.m
// lights
//
// Created by Jim on 1/25/10.
// Copyright (c) 2010 Jim Kang/Phalange Software.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#import "OGColorTools.h"
// Math functions used by OGColorTools.
@interface OGMath : NSObject
{
}
+ (NSInteger)lowestOfA:(NSInteger)a b:(NSInteger)b c:(NSInteger)c;
+ (NSInteger)highestOfA:(NSInteger)a b:(NSInteger)b c:(NSInteger)c;
+ (CGFloat)highestFloatOfA:(CGFloat)a b:(CGFloat)b c:(CGFloat)c;
+ (BOOL)pointA:(CGPoint)ptA isWithinDistance:(CGFloat)distance ofPointB:(CGPoint)ptB;
@end
@implementation OGMath
+ (NSInteger)lowestOfA:(NSInteger)a b:(NSInteger)b c:(NSInteger)c
{
if (a < b)
{
if (a < c)
{
return a;
}
else
{
return c;
}
}
else
{
if (b < c)
{
return b;
}
else
{
return c;
}
}
}
+ (NSInteger)highestOfA:(NSInteger)a b:(NSInteger)b c:(NSInteger)c
{
if (a > b)
{
if (a > c)
{
return a;
}
else
{
return c;
}
}
else
{
if (b > c)
{
return b;
}
else
{
return c;
}
}
}
+ (CGFloat)lowestFloatOfA:(CGFloat)a b:(CGFloat)b c:(CGFloat)c
{
if (a < b)
{
if (a < c)
{
return a;
}
else
{
return c;
}
}
else
{
if (b < c)
{
return b;
}
else
{
return c;
}
}
}
+ (CGFloat)highestFloatOfA:(CGFloat)a b:(CGFloat)b c:(CGFloat)c
{
if (a > b)
{
if (a > c)
{
return a;
}
else
{
return c;
}
}
else
{
if (b > c)
{
return b;
}
else
{
return c;
}
}
}
+ (BOOL)pointA:(CGPoint)ptA isWithinDistance:(CGFloat)distance ofPointB:(CGPoint)ptB
{
CGFloat deltaX = ptA.x - ptB.x;
CGFloat deltaY = ptA.y - ptB.y;
CGFloat hypotenuse = sqrt(deltaX * deltaX + deltaY * deltaY);
return (hypotenuse <= distance);
}
@end
// OGColorTools has the methods that handle hue shifting.
@implementation OGColorTools
+ (GLubyte)addColorComponent:(NSInteger)component to:(NSInteger)otherComponent
{
if (component + otherComponent > 255)
{
return 255;
}
if (component + otherComponent < 0)
{
return 0;
}
return component + otherComponent;
}
// Need this to do some generic programming involving ccColor3B.
+ (GLubyte)valueOfComponent:(ColorComponent)comp ofColor:(ccColor3B)clr
{
switch (comp)
{
case kRedComp:
return clr.r;
case kGreenComp:
return clr.g;
case kBlueComp:
default:
return clr.b;
}
}
+ (void)setComponent:(ColorComponent)comp ofColor:(ccColor3B *)clr toValue:(GLubyte)value
{
switch (comp)
{
case kRedComp:
clr->r = value;
break;
case kGreenComp:
clr->g = value;
break;
case kBlueComp:
clr->b = value;
break;
}
}
+ (ColorComponent)componentToTheRightOf:(ColorComponent)comp
{
switch (comp)
{
case kRedComp:
return kGreenComp;
case kGreenComp:
return kBlueComp;
case kBlueComp:
default:
return kRedComp;
}
}
+ (ColorComponent)componentToTheLeftOf:(ColorComponent)comp
{
switch (comp)
{
case kRedComp:
return kBlueComp;
case kGreenComp:
return kRedComp;
case kBlueComp:
default:
return kGreenComp;
}
}
+ (ccColor3B)color:(ccColor3B)clr
shiftHue:(CGFloat)percent
inDirection:(HueShiftDirection)direction
{
// This is the color we'll return, transformed by the end of the method.
ccColor3B newColor = clr;
// Find the upper and lower limits among the RGB components of this color.
NSInteger bottom = [OGMath lowestOfA:clr.r b:clr.g c:clr.b];
NSInteger top = [OGMath highestOfA:clr.r b:clr.g c:clr.b];
// Use the provided percentage parameter and limis to determine how far
// to move a component.
NSInteger range = top - bottom;
NSInteger delta = range * percent / 100.0f;
// Iterate over each color component (r, g, and b), and decide whether each
// should ascend or descend depending on its value and its neighboring component's
// value. Then, apply the ascending or descending change, if there should be one.
ColorComponent comp = kRedComp; // Start with red.
for (int i = 0; i < 3; ++i)
{
// Get the component value out of the color.
GLubyte compValue = [OGColorTools valueOfComponent:comp ofColor:clr];
// Get the component that is to the "right" of the current component if we're
// shifting in the red-to-blue direction. (e.g. If the current component is red, the
// component to the "right" will be green.) Otherwise, get the component to the left.
ColorComponent nextComp = (direction == kRedToBlue) ?
[OGColorTools componentToTheRightOf:comp] : [OGColorTools componentToTheLeftOf:comp];
// Get the next component's value out of the color.
GLubyte nextCompValue = [OGColorTools valueOfComponent:nextComp ofColor:clr];
// If this component is not at the the lower limit and the next component is at the
// upper limit, this component should descend.
NSInteger componentDelta = 0;
if ((compValue > bottom) && (nextCompValue == top))
{
componentDelta = -delta;
}
// If this component is not at the upper limit and the next component is at the
// lower limit, this component should ascend.
else if ((compValue < top) && (nextCompValue == bottom))
{
componentDelta = delta;
}
// Otherwise, this component does not need to move.
if (componentDelta)
{
[OGColorTools setComponent:comp
ofColor:&newColor
toValue:[OGColorTools addColorComponent:componentDelta to:compValue]];
}
// Move on to the next component.
comp = nextComp;
}
return newColor;
}
@end
//
// OGHueShiftExampleLayer.h
// OGColorShift
//
// Created by Jim on 9/18/10.
// Copyright Phalange Software 2010.
//
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// This is an example scene based on the boilerplate cocos2d HelloWorld
// scene that demonstrates a simple use of the hue shifting method in
// OGColorTools. It will load a sprite, set its color, then shift its
// hue every time the layer is tapped.
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
// HelloWorld Layer
@interface OGHueShiftExampleLayer : CCLayer
{
CCSprite *targetSprite;
}
@property (nonatomic, retain) CCSprite *targetSprite;
// returns a Scene that contains the HelloWorld as the only child
+(id) scene;
@end
//
// OGHueShiftExampleLayer.m
// OGColorShift
//
// Created by Jim on 9/18/10.
// Copyright Phalange Software 2010.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// Import the interfaces
#import "OGHueShiftExampleLayer.h"
#import "OGColorTools.h"
// HelloWorld implementation
@implementation OGHueShiftExampleLayer
@synthesize targetSprite;
+(id) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
OGHueShiftExampleLayer *layer = [OGHueShiftExampleLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {
// create and initialize a Label
CCLabel* label = [CCLabel labelWithString:@"Phalange Color Shift example"
fontName:@"Marker Felt" fontSize:24];
// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];
label.position = ccp(size.width/2 , size.height/4);
[self addChild:label];
// Add a sprite whose hue will shift. (Replace this code with code that
// loads your own sprite.)
CCSpriteSheet *spriteManager = [[CCSpriteSheet alloc]
initWithFile:@"icon.png"
capacity:1];
[self addChild:spriteManager];
self.targetSprite = [CCSprite spriteWithSpriteSheet:spriteManager
rect:CGRectMake(0, 0, 57, 57)];
self.targetSprite.position = ccp(size.width/2 , size.height/2);
[spriteManager addChild:self.targetSprite];
// Set the sprite's color.
self.targetSprite.color = ccGREEN;
// Register for touches.
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
priority:0
swallowsTouches:YES];
}
return self;
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
[targetSprite release];
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
#pragma mark TargetedTouchDelegate implementation
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
// Shift the hue of the target sprite every time the layer is touched.
// The swipe was more horizontal than vertical, so do a hue shift.
self.targetSprite.color = [OGColorTools color:self.targetSprite.color
shiftHue:100.0f
inDirection:kRedToBlue];
return NO;
}
@end
@jimkang
Copy link
Author

jimkang commented Sep 18, 2010

This is an example of implementing hue shifting in cocos2d as it was explained in this post. OGColorTools contains the code that does the color shifting, and OGHueShiftExampleLayer is a simple example layer that creates a sprite and shifts its color every time the layer is tapped. You will need to provide your own bitmap and load it into the sprite for this to work.

@ohenak
Copy link

ohenak commented Aug 28, 2012

Hi @jimkang, nice observation about hue shifting. But does this support having percentage larger than 100?
I afraid it didn't handle the case yet. But this is really nice and clear implementation.

Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment