I'm trying to create a SKEffectNode that will turn transparent any green pixel over a black background. For testing purposes while I figure this stuff out, I want to make sure that the following code will not turn anything transparent within the subtree of the SKEffectNode. The following code actually prevents the child from being drawn and it spits up the following error:
CIColorCube inputCubeData is not of the expected length.
That's the method that creates the SKEffectNode
- (SKEffectNode *) newVeil
{
SKEffectNode *node = [[SKEffectNode alloc] init];
node.shouldEnableEffects = YES;
node.filter = [self createFilter];
SKSpriteNode *darkness = [SKSpriteNode spriteNodeWithColor:[UIColor blackColor] size:self.view.frame.size];
node.position = self.view.center;
[node addChild:darkness];
return node;
}
That's how I setup the filter (most, or dare I say all of this code is in Apple's dev documents).
- (CIFilter *) createFilter
{
// Allocate memory
const unsigned int size = 64;
float *cubeData = (float *)malloc (size * size * size * sizeof (float) * 4);
float *c = cubeData;
rgb rgbInput;
hsv hsvOutput;
// Populate cube with a simple gradient going from 0 to 1
for (int z = 0; z < size; z++){
rgbInput.b = ((double)z)/(size-1); // Blue value
for (int y = 0; y < size; y++){
rgbInput.g = ((double)y)/(size-1); // Green value
for (int x = 0; x < size; x ++){
rgbInput.r = ((double)x)/(size-1); // Red value
// Convert RGB to HSV
// You can find publicly available rgbToHSV functions on the Internet
hsvOutput = rgb2hsv(rgbInput);
// Use the hue value to determine which to make transparent
// The minimum and maximum hue angle depends on
// the color you want to remove
float alpha = (hsvOutput.h > 120 && hsvOutput.h < 100) ? 0.0f: 1.0f;
// Calculate premultiplied alpha values for the cube
c[0] = rgbInput.b * alpha;
c[1] = rgbInput.g * alpha;
c[2] = rgbInput.r * alpha;
c[3] = alpha;
c += 4; // advance our pointer into memory for the next color value
}
}
}
// Create memory with the cube data
NSData *data = [NSData dataWithBytesNoCopy:cubeData
length:size
freeWhenDone:YES];
CIFilter *colorCube = [CIFilter filterWithName:@"CIColorCube"];
[colorCube setValue:@(size) forKey:@"inputCubeDimension"];
// Set data for cube
[colorCube setValue:data forKey:@"inputCubeData"];
return colorCube;
}
I just can't spot the problem. Not a whole lot of experience with CoreImage. Anyone?
Update 1
I tried exporting the whole CIFilter into it's own class.
// PMColorCube.h
#import <CoreImage/CoreImage.h>
@interface PMColorCube : CIFilter{
CIImage *inputImage;
}
@property (retain, nonatomic) CIImage *inputImage;
@end
// PMColorCube.m
#import "PMColorCube.h"
typedef struct {
double r; // percent
double g; // percent
double b; // percent
} rgb;
typedef struct {
double h; // angle in degrees
double s; // percent
double v; // percent
} hsv;
static hsv rgb2hsv(rgb in);
@implementation PMColorCube
@synthesize inputImage;
hsv rgb2hsv(rgb in)
{
hsv out;
double min, max, delta;
min = in.r < in.g ? in.r : in.g;
min = min < in.b ? min : in.b;
max = in.r > in.g ? in.r : in.g;
max = max > in.b ? max : in.b;
out.v = max; // v
delta = max - min;
if( max > 0.0 ) {
out.s = (delta / max); // s
} else {
// r = g = b = 0 // s = 0, v is undefined
out.s = 0.0;
out.h = NAN; // its now undefined
return out;
}
if( in.r >= max ) // > is bogus, just keeps compilor happy
out.h = ( in.g - in.b ) / delta; // between yellow & magenta
else
if( in.g >= max )
out.h = 2.0 + ( in.b - in.r ) / delta; // between cyan & yellow
else
out.h = 4.0 + ( in.r - in.g ) / delta; // between magenta & cyan
out.h *= 60.0; // degrees
if( out.h < 0.0 )
out.h += 360.0;
return out;
}
- (CIImage *) outputImage
{
const unsigned int size = 64;
float *cubeData = (float *)malloc (size * size * size * sizeof (float) * 4);
float *c = cubeData;
rgb rgbInput;
hsv hsvOutput;
// Populate cube with a simple gradient going from 0 to 1
for (int z = 0; z < size; z++){
rgbInput.b = ((double)z)/(size-1); // Blue value
for (int y = 0; y < size; y++){
rgbInput.g = ((double)y)/(size-1); // Green value
for (int x = 0; x < size; x ++){
rgbInput.r = ((double)x)/(size-1); // Red value
// Convert RGB to HSV
// You can find publicly available rgbToHSV functions on the Internet
hsvOutput = rgb2hsv(rgbInput);
// Use the hue value to determine which to make transparent
// The minimum and maximum hue angle depends on
// the color you want to remove
float alpha = (hsvOutput.h > 120 && hsvOutput.h < 100) ? 0.0f: 1.0f;
// Calculate premultiplied alpha values for the cube
c[0] = rgbInput.b * alpha;
c[1] = rgbInput.g * alpha;
c[2] = rgbInput.r * alpha;
c[3] = alpha;
c += 4; // advance our pointer into memory for the next color value
}
}
}
// Create memory with the cube data
NSData *data = [NSData dataWithBytesNoCopy:cubeData
length:size
freeWhenDone:YES];
CIFilter *colorCube = [CIFilter filterWithName:@"CIColorCube"];
[colorCube setValue:@(size) forKey:@"inputCubeDimension"];
// Set data for cube
[colorCube setValue:data forKey:@"inputCubeData"];
[colorCube setValue:self.inputImage forKey:kCIInputImageKey];
CIImage *result = [colorCube valueForKey:kCIOutputImageKey];
return result;
}
@end
I still have the same error during run time
Embarrassing as it may sound. The size that I pass in the class method when creating the NSData doesn't correspond to the real size. Fixed it like so:
// Create memory with the cube data
NSData *data = [NSData dataWithBytesNoCopy:cubeData
length:size * size * size * sizeof (float) * 4
freeWhenDone:YES];