1// Copyright 2013 Howling Moon Software. All rights reserved. 2// See http://chipmunk2d.net/legal.php for more information. 3 4// 5// ChipmunkImageSampler.m 6// DeformableChipmunk 7// 8// Created by Scott Lembcke on 8/26/11. 9// Copyright 2011 __MyCompanyName__. All rights reserved. 10// 11 12#import <TargetConditionals.h> 13 14#if TARGET_OS_IPHONE == 1 15 #import <ImageIO/ImageIO.h> 16#endif 17 18#import "ChipmunkImageSampler.h" 19 20@implementation ChipmunkBitmapSampler 21 22@synthesize width = _width, height = _height, bytesPerPixel = _bytesPerPixel, component = _component, pixelData = _pixelData, outputRect = _outputRect; 23 24// Much faster than (int)floor(f) 25// Profiling showed floor() to be a sizable performance hog 26static inline int 27floor_int(cpFloat f) 28{ 29 int i = (int)f; 30 return (f < 0.0f && f != i ? i - 1 : i); 31} 32 33// TODO finish this? 34//static inline cpFloat 35//SampleFunc4444(cpVect point, ChipmunkImageSampler *self) 36//{ 37// int x = (int)point.x; 38// int y = (int)point.y - (self->_flip ? self->_height - 1 : 0); 39// 40// int com = self->_component; 41// int byte = self->_pixels[y*self->_stride + x*self->_bytesPerPixel + com/2]; 42// int value = 43// return (cpFloat)(byte>>())/15.0; 44//} 45 46static cpFloat 47SampleFunc8Clamp(cpVect point, ChipmunkBitmapSampler *self) 48{ 49 unsigned long w = self->_width; 50 unsigned long h = self->_height; 51 52 cpBB bb = self->_outputRect; 53 cpVect clamped = cpBBClampVect(bb, point); 54 55 unsigned long x = floor_int((w - 1)*(clamped.x - bb.l)/(bb.r - bb.l) + 0.5); 56 unsigned long y = floor_int((h - 1)*(clamped.y - bb.b)/(bb.t - bb.b) + 0.5); 57 58 if(self->_flip) y = h - 1 - y; 59 60// printf("(%6.2f, %6.2f) -> (% 4d, % 4d) : %d\n", point.x, point.y, x, y, self->_pixels[y*self->_stride + x*self->_bytesPerPixel + self->_component]); 61 return (cpFloat)self->_pixels[y*self->_stride + x*self->_bytesPerPixel + self->_component]/255.0; 62} 63 64static cpFloat 65SampleFunc8Border(cpVect point, ChipmunkBitmapSampler *self) 66{ 67 unsigned long w = self->_width; 68 unsigned long h = self->_height; 69 70 cpBB bb = self->_outputRect; 71 if(cpBBContainsVect(bb, point)){ 72 unsigned long x = floor_int((w - 1)*(point.x - bb.l)/(bb.r - bb.l) + 0.5); 73 unsigned long y = floor_int((h - 1)*(point.y - bb.b)/(bb.t - bb.b) + 0.5); 74 75 if(self->_flip) y = h - 1 - y; 76 77// printf("(%6.2f, %6.2f) -> (% 4d, % 4d)\n", point.x, point.y, x, y); 78 return (cpFloat)self->_pixels[y*self->_stride + x*self->_bytesPerPixel + self->_component]/255.0; 79 } else { 80 return self->_borderValue; 81 } 82} 83 84-(id)initWithWidth:(NSUInteger)width height:(NSUInteger)height stride:(NSUInteger)stride bytesPerPixel:(NSUInteger)bytesPerPixel component:(NSUInteger)component flip:(bool)flip pixelData:(NSData *)pixelData 85{ 86 if((self = [super initWithSamplingFunction:(cpMarchSampleFunc)SampleFunc8Clamp])){ 87 _width = width; 88 _height = height; 89 _stride = stride; 90 91 _bytesPerPixel = bytesPerPixel; 92 _component = component; 93 94 _flip = flip; 95 _pixelData = [pixelData retain]; 96 _pixels = [pixelData bytes]; 97 98 _outputRect = cpBBNew(0.5, 0.5, self.width - 0.5, self.height - 0.5); 99 } 100 101 return self; 102} 103 104 105- (void)dealloc 106{ 107 [_pixelData release]; 108 109 [super dealloc]; 110} 111 112-(void)setBorderRepeat 113{ 114 _sampleFunc = (cpMarchSampleFunc)SampleFunc8Clamp; 115} 116 117-(void)setBorderValue:(cpFloat)borderValue 118{ 119 _sampleFunc = (cpMarchSampleFunc)SampleFunc8Border; 120 _borderValue = borderValue; 121} 122 123static cpBB 124BorderedBB(cpBB bb, NSUInteger width, NSUInteger height) 125{ 126 cpFloat xBorder = (bb.r - bb.l)/(cpFloat)(width - 1); 127 cpFloat yBorder = (bb.t - bb.b)/(cpFloat)(height - 1); 128 129 return cpBBNew(bb.l - xBorder, bb.b - yBorder, bb.r + xBorder, bb.t + yBorder); 130} 131 132-(ChipmunkPolylineSet *)marchAllWithBorder:(bool)bordered hard:(bool)hard 133{ 134 NSUInteger width = self.width; 135 NSUInteger height = self.height; 136 cpBB bb = self.outputRect; 137 138 if(bordered){ 139 return [self march:BorderedBB(bb, width, height) xSamples:width+2 ySamples:height+2 hard:hard]; 140 } else { 141 return [self march:bb xSamples:width ySamples:height hard:hard]; 142 } 143} 144 145@end 146 147 148 149@implementation ChipmunkCGContextSampler 150 151@synthesize context = _context; 152 153-(NSMutableData *)pixelData {return (NSMutableData *)super.pixelData;} 154 155-(id)initWithWidth:(unsigned long)width height:(unsigned long)height colorSpace:(CGColorSpaceRef)colorSpace bitmapInfo:(CGBitmapInfo)bitmapInfo component:(NSUInteger)component 156{ 157 // Need to create a context to get info about the context. 158 // If you let the context allocate it's own memory it seems to move it around. O_o 159 CGContextRef temp = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, bitmapInfo); 160 cpAssertHard(temp, "Failed to create temporary CGBitmapContext"); 161 162 unsigned long bpc = CGBitmapContextGetBitsPerComponent(temp); 163 unsigned long bpp = CGBitmapContextGetBitsPerPixel(temp)/8; 164 cpAssertHard(bpc == 8, "Cannot handle non-8bit-per-pixel bitmap data!"); 165 166 CGContextRelease(temp); 167 168 unsigned long stride = width*bpp; 169 NSMutableData *pixelData = [NSMutableData dataWithLength:stride*height]; 170 _context = CGBitmapContextCreate([pixelData mutableBytes], width, height, bpc, stride, colorSpace, bitmapInfo); 171 172 return [self initWithWidth:width height:height stride:stride bytesPerPixel:bpp component:component flip:TRUE pixelData:pixelData]; 173} 174 175-(void)dealloc 176{ 177 CGContextRelease(_context); 178 179 [super dealloc]; 180} 181 182@end 183 184 185 186@implementation ChipmunkImageSampler 187 188+(CGImageRef)loadImage:(NSURL *)url 189{ 190 CGImageSourceRef image_source = CGImageSourceCreateWithURL((CFURLRef)url, NULL); 191 CGImageRef image = CGImageSourceCreateImageAtIndex(image_source, 0, NULL); 192 cpAssertHard(image, "Image %s could not be loaded.", [[url description] UTF8String]); 193 194 CFRelease(image_source); 195 return image; 196} 197 198-(id)initWithImage:(CGImageRef)image isMask:(bool)isMask contextWidth:(NSUInteger)width contextHeight:(NSUInteger)height 199{ 200 if(width == 0) width = CGImageGetWidth(image); 201 if(height == 0) height = CGImageGetHeight(image); 202 203 CGColorSpaceRef colorSpace = (isMask ? CGColorSpaceCreateDeviceGray() : NULL); 204 CGBitmapInfo bitmapInfo = (CGBitmapInfo)(isMask ? kCGImageAlphaNone : kCGImageAlphaOnly); 205 206 if((self = [super initWithWidth:width height:height colorSpace:colorSpace bitmapInfo:bitmapInfo component:0])){ 207 CGContextDrawImage(self.context, CGRectMake(0, 0, width, height), image); 208 } 209 210 CGColorSpaceRelease(colorSpace); 211 212 return self; 213} 214 215-(id)initWithImageFile:(NSURL *)url isMask:(bool)isMask 216{ 217 CGImageRef image = [[self class] loadImage:url]; 218 unsigned long width = CGImageGetWidth(image); 219 unsigned long height = CGImageGetHeight(image); 220 221 self = [self initWithImage:image isMask:isMask contextWidth:width contextHeight:height]; 222 223 CGImageRelease(image); 224 225 return self; 226} 227 228+(ChipmunkImageSampler *)samplerWithImageFile:(NSURL *)url isMask:(bool)isMask 229{ 230 return [[[self alloc] initWithImageFile:url isMask:isMask] autorelease]; 231} 232 233@end 234