1 // Copyright 2013 Howling Moon Software. All rights reserved.
2 // See http://chipmunk2d.net/legal.php for more information.
5 // ChipmunkImageSampler.m
8 // Created by Scott Lembcke on 8/26/11.
9 // Copyright 2011 __MyCompanyName__. All rights reserved.
12 #import <TargetConditionals.h>
14 #if TARGET_OS_IPHONE == 1
15 #import <ImageIO/ImageIO.h>
18 #import "ChipmunkImageSampler.h"
20 @implementation ChipmunkBitmapSampler
22 @synthesize width = _width, height = _height, bytesPerPixel = _bytesPerPixel, component = _component, pixelData = _pixelData, outputRect = _outputRect;
24 // Much faster than (int)floor(f)
25 // Profiling showed floor() to be a sizable performance hog
30 return (f < 0.0f && f != i ? i - 1 : i);
34 //static inline cpFloat
35 //SampleFunc4444(cpVect point, ChipmunkImageSampler *self)
37 // int x = (int)point.x;
38 // int y = (int)point.y - (self->_flip ? self->_height - 1 : 0);
40 // int com = self->_component;
41 // int byte = self->_pixels[y*self->_stride + x*self->_bytesPerPixel + com/2];
43 // return (cpFloat)(byte>>())/15.0;
47 SampleFunc8Clamp(cpVect point, ChipmunkBitmapSampler *self)
49 unsigned long w = self->_width;
50 unsigned long h = self->_height;
52 cpBB bb = self->_outputRect;
53 cpVect clamped = cpBBClampVect(bb, point);
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);
58 if(self->_flip) y = h - 1 - y;
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;
65 SampleFunc8Border(cpVect point, ChipmunkBitmapSampler *self)
67 unsigned long w = self->_width;
68 unsigned long h = self->_height;
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);
75 if(self->_flip) y = h - 1 - y;
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;
80 return self->_borderValue;
84 -(id)initWithWidth:(NSUInteger)width height:(NSUInteger)height stride:(NSUInteger)stride bytesPerPixel:(NSUInteger)bytesPerPixel component:(NSUInteger)component flip:(bool)flip pixelData:(NSData *)pixelData
86 if((self = [super initWithSamplingFunction:(cpMarchSampleFunc)SampleFunc8Clamp])){
91 _bytesPerPixel = bytesPerPixel;
92 _component = component;
95 _pixelData = [pixelData retain];
96 _pixels = [pixelData bytes];
98 _outputRect = cpBBNew(0.5, 0.5, self.width - 0.5, self.height - 0.5);
107 [_pixelData release];
112 -(void)setBorderRepeat
114 _sampleFunc = (cpMarchSampleFunc)SampleFunc8Clamp;
117 -(void)setBorderValue:(cpFloat)borderValue
119 _sampleFunc = (cpMarchSampleFunc)SampleFunc8Border;
120 _borderValue = borderValue;
124 BorderedBB(cpBB bb, NSUInteger width, NSUInteger height)
126 cpFloat xBorder = (bb.r - bb.l)/(cpFloat)(width - 1);
127 cpFloat yBorder = (bb.t - bb.b)/(cpFloat)(height - 1);
129 return cpBBNew(bb.l - xBorder, bb.b - yBorder, bb.r + xBorder, bb.t + yBorder);
132 -(ChipmunkPolylineSet *)marchAllWithBorder:(bool)bordered hard:(bool)hard
134 NSUInteger width = self.width;
135 NSUInteger height = self.height;
136 cpBB bb = self.outputRect;
139 return [self march:BorderedBB(bb, width, height) xSamples:width+2 ySamples:height+2 hard:hard];
141 return [self march:bb xSamples:width ySamples:height hard:hard];
149 @implementation ChipmunkCGContextSampler
151 @synthesize context = _context;
153 -(NSMutableData *)pixelData {return (NSMutableData *)super.pixelData;}
155 -(id)initWithWidth:(unsigned long)width height:(unsigned long)height colorSpace:(CGColorSpaceRef)colorSpace bitmapInfo:(CGBitmapInfo)bitmapInfo component:(NSUInteger)component
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");
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!");
166 CGContextRelease(temp);
168 unsigned long stride = width*bpp;
169 NSMutableData *pixelData = [NSMutableData dataWithLength:stride*height];
170 _context = CGBitmapContextCreate([pixelData mutableBytes], width, height, bpc, stride, colorSpace, bitmapInfo);
172 return [self initWithWidth:width height:height stride:stride bytesPerPixel:bpp component:component flip:TRUE pixelData:pixelData];
177 CGContextRelease(_context);
186 @implementation ChipmunkImageSampler
188 +(CGImageRef)loadImage:(NSURL *)url
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]);
194 CFRelease(image_source);
198 -(id)initWithImage:(CGImageRef)image isMask:(bool)isMask contextWidth:(NSUInteger)width contextHeight:(NSUInteger)height
200 if(width == 0) width = CGImageGetWidth(image);
201 if(height == 0) height = CGImageGetHeight(image);
203 CGColorSpaceRef colorSpace = (isMask ? CGColorSpaceCreateDeviceGray() : NULL);
204 CGBitmapInfo bitmapInfo = (CGBitmapInfo)(isMask ? kCGImageAlphaNone : kCGImageAlphaOnly);
206 if((self = [super initWithWidth:width height:height colorSpace:colorSpace bitmapInfo:bitmapInfo component:0])){
207 CGContextDrawImage(self.context, CGRectMake(0, 0, width, height), image);
210 CGColorSpaceRelease(colorSpace);
215 -(id)initWithImageFile:(NSURL *)url isMask:(bool)isMask
217 CGImageRef image = [[self class] loadImage:url];
218 unsigned long width = CGImageGetWidth(image);
219 unsigned long height = CGImageGetHeight(image);
221 self = [self initWithImage:image isMask:isMask contextWidth:width contextHeight:height];
223 CGImageRelease(image);
228 +(ChipmunkImageSampler *)samplerWithImageFile:(NSURL *)url isMask:(bool)isMask
230 return [[[self alloc] initWithImageFile:url isMask:isMask] autorelease];