Skip to content

Instantly share code, notes, and snippets.

@kaiix
Created May 28, 2012 11:55
Show Gist options
  • Save kaiix/2818762 to your computer and use it in GitHub Desktop.
Save kaiix/2818762 to your computer and use it in GitHub Desktop.
Gloss gradients button for ios
//
// GlossGradientButton.h
// iOS version of gloss gradients button
// original version for Mac OSX http://cocoawithlove.com/2008/09/drawing-gloss-gradients-in-coregraphics.html
//
#import <UIKit/UIKit.h>
@interface GlossGradientButton : UIButton {
UIColor *buttonColor_;
}
@property(nonatomic, retain) UIColor *buttonColor;
- (id)initWithFrame:(CGRect)frame color:(UIColor *)color;
@end
//
// GlossGradientButton.m
// iOS version of gloss gradients button
// original version for Mac OSX http://cocoawithlove.com/2008/09/drawing-gloss-gradients-in-coregraphics.html
//
#import "GlossGradientButton.h"
float perceptualGlossFractionForColor(float *inputComponents)
{
const float REFLECTION_SCALE_NUMBER = 0.2;
const float NTSC_RED_FRACTION = 0.299;
const float NTSC_GREEN_FRACTION = 0.587;
const float NTSC_BLUE_FRACTION = 0.114;
float glossScale =
NTSC_RED_FRACTION * inputComponents[0] +
NTSC_GREEN_FRACTION * inputComponents[1] +
NTSC_BLUE_FRACTION * inputComponents[2];
glossScale = pow(glossScale, REFLECTION_SCALE_NUMBER);
return glossScale;
}
typedef struct
{
float color[4];
float caustic[4];
float expCoefficient;
float expScale;
float expOffset;
float initialWhite;
float finalWhite;
} GlossParameters;
static void glossInterpolation(void *info, const float *input,
float *output)
{
GlossParameters *params = (GlossParameters *)info;
float progress = *input;
if (progress < 0.5)
{
progress = progress * 2.0;
progress =
1.0 - params->expScale * (expf(progress * -params->expCoefficient) - params->expOffset);
float currentWhite = progress * (params->finalWhite - params->initialWhite) + params->initialWhite;
output[0] = params->color[0] * (1.0 - currentWhite) + currentWhite;
output[1] = params->color[1] * (1.0 - currentWhite) + currentWhite;
output[2] = params->color[2] * (1.0 - currentWhite) + currentWhite;
output[3] = params->color[3] * (1.0 - currentWhite) + currentWhite;
}
else
{
progress = (progress - 0.5) * 2.0;
progress = params->expScale *
(expf((1.0 - progress) * -params->expCoefficient) - params->expOffset);
output[0] = params->color[0] * (1.0 - progress) + params->caustic[0] * progress;
output[1] = params->color[1] * (1.0 - progress) + params->caustic[1] * progress;
output[2] = params->color[2] * (1.0 - progress) + params->caustic[2] * progress;
output[3] = params->color[3] * (1.0 - progress) + params->caustic[3] * progress;
}
}
void getComponents(UIColor *color, float *output)
{
[color getRed:&output[0] green:&output[1] blue:&output[2] alpha:&output[3]];
}
void perceptualCausticColorForColor(float *inputComponents, float *outputComponents)
{
const float CAUSTIC_FRACTION = 0.60;
const float COSINE_ANGLE_SCALE = 1.4;
const float MIN_RED_THRESHOLD = 0.95;
const float MAX_BLUE_THRESHOLD = 0.7;
const float GRAYSCALE_CAUSTIC_SATURATION = 0.2;
UIColor *source = [UIColor colorWithRed:inputComponents[0]
green:inputComponents[1]
blue:inputComponents[2]
alpha:inputComponents[3]];
float hue, saturation, brightness, alpha;
[source getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha];
float targetHue, targetSaturation, targetBrightness;
[[UIColor yellowColor] getHue:&targetHue saturation:&targetSaturation brightness:&targetBrightness alpha:&alpha];
if (saturation < 1e-3) {
hue = targetHue;
saturation = GRAYSCALE_CAUSTIC_SATURATION;
}
if (hue > MIN_RED_THRESHOLD) {
hue -= 1.0;
} else if (hue > MAX_BLUE_THRESHOLD) {
[[UIColor magentaColor] getHue:&targetHue saturation:&targetSaturation brightness:&targetBrightness alpha:&alpha];
}
float scaledCaustic = CAUSTIC_FRACTION * 0.5 * (1.0 + cos(COSINE_ANGLE_SCALE * M_PI * (hue - targetHue)));
UIColor *targetColor = [UIColor colorWithHue:hue * (1.0 - scaledCaustic) + targetHue * scaledCaustic
saturation:saturation
brightness:brightness * (1.0 - scaledCaustic) + targetBrightness * scaledCaustic
alpha:inputComponents[3]];
getComponents(targetColor, outputComponents);
}
void DrawGlossGradient(CGContextRef context, UIColor *color, CGRect inRect)
{
const float EXP_COEFFICIENT = 1.2;
const float REFLECTION_MAX = 0.60;
const float REFLECTION_MIN = 0.20;
GlossParameters params;
params.expCoefficient = EXP_COEFFICIENT;
params.expOffset = expf(-params.expCoefficient);
params.expScale = 1.0 / (1.0 - params.expOffset);
getComponents(color, params.color);
perceptualCausticColorForColor(params.color, params.caustic);
float glossScale = perceptualGlossFractionForColor(params.color);
params.initialWhite = glossScale * REFLECTION_MAX;
params.finalWhite = glossScale * REFLECTION_MIN;
static const float input_value_range[2] = {0, 1};
static const float output_value_ranges[8] = {0, 1, 0, 1, 0, 1, 0, 1};
CGFunctionCallbacks callbacks = {0, glossInterpolation, NULL};
CGFunctionRef gradientFunction = CGFunctionCreate(
(void *)&params,
1, // number of input values to the callback
input_value_range,
4, // number of components (r, g, b, a)
output_value_ranges,
&callbacks);
CGPoint startPoint = CGPointMake(CGRectGetMinX(inRect), CGRectGetMaxY(inRect));
CGPoint endPoint = CGPointMake(CGRectGetMinX(inRect), CGRectGetMinY(inRect));
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGShadingRef shading = CGShadingCreateAxial(
colorspace, endPoint, startPoint, gradientFunction, FALSE, FALSE);
CGContextSaveGState(context);
CGContextClipToRect(context, inRect);
CGContextDrawShading(context, shading);
CGContextRestoreGState(context);
CGShadingRelease(shading);
CGColorSpaceRelease(colorspace);
CGFunctionRelease(gradientFunction);
}
@implementation GlossGradientButton
@synthesize buttonColor = buttonColor_;
- (id)initWithFrame:(CGRect)frame color:(UIColor *)color {
if (self = [super initWithFrame:frame]) {
buttonColor_ = color;
}
return self;
}
- (void)drawRect:(CGRect)rect {
UIBezierPath *roundRectPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
cornerRadius:5];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
[roundRectPath addClip];
DrawGlossGradient(context, self.buttonColor, self.bounds);
if (self.isHighlighted) {
[[UIColor colorWithRed:0.0 green:0.15 blue:0.35 alpha:0.5] setFill];
[roundRectPath fill];
}
CGContextRestoreGState(context);
[[UIColor colorWithWhite:1.0 alpha:0.234] setStroke];
[roundRectPath setLineWidth:0.5];
[roundRectPath stroke];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment