class library: Spawner - don't access PriorityQueue-array
[supercollider.git] / editors / scapp / SCImage.M
blobf23a43800ed74c8c1298f1ecd16e19689100d48e
1 //
2 // SCImage.M
3 // SCImage primitive set for SuperCollider3
4 //
5 // blackrain 2008
6 // charles picasso 2008
7 //
10 //#if SCIMAGE_MAC_OS_10_4
12 #import "SCImage.h"
13 #import <sys/time.h>
14 #import <pthread.h>
15 #import <objc/objc.h>
16 #import <objc/objc-class.h>
17 #import <ctype.h>
19 #import "SCBase.h"
20 #import "PyrSymbol.h"
21 #import "PyrPrimitive.h"
22 #import "PyrObject.h"
23 #import "PyrSlot.h"
24 #import "PyrKernel.h"
25 #import "VMGlobals.h"
26 #import "SCGeom.h"
27 #import "SCVirtualMachine.h"
28 #import "GC.h"
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <math.h>
34 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4)
35 #define class_getSuperclass(a) ((a)->super_class)
36 #define class_getName(a) ((a)->name)
37 typedef float ColorData;
38 #else
39 typedef CGFloat ColorData;
40 #endif
42 extern int slotGetPoint(PyrSlot* a, NSPoint *p);
43 extern int slotGetNSRect(PyrSlot* a, NSRect *r);
44 extern int allocSlotStrVal(PyrSlot *slot, char **str);
45 extern int slotColorVal(PyrSlot *slot, SCColor *sccolor);
46 extern int slotStrLen(PyrSlot* slot);
48 extern pthread_mutex_t gLangMutex; // in case
49 PyrSymbol *s_scimage;
50 PyrSymbol *s_scfilter;
51 PyrSymbol *s_scimagekernel;
52 static BOOL gCIFilterPlugInsLoaded = NO; //
54 CGRect OutsetRect(CGRect r, const float dx, const float dy);
57 * SCImage Finalizer callback function
59 int FinalizeSCImageObject(struct VMGlobals* g, struct PyrObject* object)
61 if( !IsNil(object->slots+0) ) {
62 SCImage * scimage = (SCImage*)slotRawPtr(object->slots);
63 #if SCIMAGE_DEBUG
64 post("finalize scimage (%p), dataptr (%p)\n", object, scimage);
65 #endif
66 [scimage release];
67 SetNil(object->slots+0);
69 return errNone;
73 * SCImageKernel Finalizer callback function
75 int FinalizeSCImageKernelObject(struct VMGlobals* g, struct PyrObject* object)
77 if( !IsNil(object->slots+4) ) {
78 CIKernel * kernel = (CIKernel*)slotRawPtr(&object->slots[4]);
79 #if SCIMAGE_DEBUG
80 post("finalize scimagekernel (%p), dataptr (%p)\n", object, kernel);
81 #endif
82 [kernel release];
83 SetNil(object->slots+4);
85 return errNone;
88 /* This function should be the one to use to create a new PyrObject of class s_scimage
89 * it will setup directly the Finalizer object
90 * so the PyrObject will free directly the SCImage instance
91 * Not calling this function in any other situation may result in a memory leak.
93 PyrObject* newPyrSCImage(VMGlobals* g)
95 PyrObject* object = instantiateObject(g->gc, s_scimage->u.classobj, 0, false, true);
96 if(object)
97 InstallFinalizer(g, object, 9, FinalizeSCImageObject);
98 return object;
101 inline NSString* nsStringFromSlot(PyrSlot *slot)
103 int len;
104 NSString *nsString;
106 len = slotStrLen(slot); // check if Symbol or String
107 if(len == -1) // returns -1 if failure
108 return nil;
110 nsString = [[NSString alloc]initWithBytes:IsSym(slot) ? (void*)slotRawSymbol(slot)->name : (void*)slotRawString(slot)->s length:len encoding:NSASCIIStringEncoding];
112 return [nsString autorelease];
115 //==================================================================================================
116 // SCImage uses now two models: NSBitmapImageRep + CIImage underneath
118 // Concerning NSBitmapImageRep
119 // ---------------------------
120 // (+) advantages : Quartz compliant - pixel access + manipulation via simple setters
121 // although NSBitmapImageRep does handle only pre-multiplied alpha pixels
122 // NSAlphaNonpremultipliedBitmapFormat does not work in 10.5 so not portable for now...
124 // Concerning CIImage
125 // ------------------
126 // (-) drawbacks : no direct pixel manipulation like the nsbitmapimagerep (wich is anyway a not very good model for that too...
127 // a CGImage should be better
128 // we may need also to download _from_ gpu memory to get the nsbitmapimagerep if CoreImage uses acceleration
129 // (+) advantages : can be in gpu memory
130 // possibility to use the CoreImage possibilities : Filters...ect...
131 //==================================================================================================
133 // Getting Pixel Macro -
134 // handle different conversions but there is roundoff errors... so
135 // integer division -> better seems to be the MultiplyAlpha
136 // change COLOR_SCALE macro to point to another function for a _forward_ alpha premultiplication
137 // change COLOR_UNSCALE macro to point to another function for a _reverse_ alpha premultiplication
138 // better to use getPixel:AtX:y because it handles the unscale really better
140 #pragma mark -- Pixel Macros
142 //#if (0)
143 static const float oneOn255 = (1.f/255.f);
146 // Get Rid of signed integer interpretation in sc - kill the 2-complement info
147 #define PIXEL_RED(pixel) ((pixel >> 24) & 0x000000FF)
148 #define PIXEL_GREEN(pixel) ((pixel >> 16) & 0x000000FF)
149 #define PIXEL_BLUE(pixel) ((pixel >> 8) & 0x000000FF)
150 #define PIXEL_ALPHA(pixel) (pixel & 0x000000FF)
152 // Setting Pixel Macro
153 #define SET_PIXEL_RED(pixel, value) pixel = (pixel & 0x00FFFFFF) | ((value) << 24)
154 #define SET_PIXEL_GREEN(pixel, value) pixel = (pixel & 0xFF00FFFF) | ((value) << 16)
155 #define SET_PIXEL_BLUE(pixel, value) pixel = (pixel & 0xFFFF00FF) | ((value) << 8)
156 #define SET_PIXEL_ALPHA(pixel, value) pixel = (pixel & 0xFFFFFF00) | (value)
158 // Macro for working with premultiplied alpha data
159 // May introduce several roundoff errors if several convert - unconvert passes so... careful with that...
160 #define COLOR_SCALE COLOR_SCALE2
161 #define COLOR_SCALE1(channel, alpha) (((channel) * (alpha)) + 127) / 255 // simple
162 #define COLOR_SCALE2(channel, alpha) MultiplyAlpha(alpha, channel)
164 // none of those are ok :(
165 #define COLOR_UNSCALE COLOR_UNSCALE3
166 #define COLOR_UNSCALE1(channel, alpha) ((channel) * 255) / alpha // simple Formula - rounding errors if scale - unscale !!! so...
167 #define COLOR_UNSCALE2(channel, alpha) ((channel) * 256 - 1) / (alpha) // ...modified one... beware
168 #define COLOR_UNSCALE3(channel, alpha) ((((channel) * 255) + ((alpha) >> 1)) / (alpha)) // should be better - from libnr - sodipodi
170 // more Macros see libnr - sodipodi here:
171 // http://sodipodi.cvs.sourceforge.net/sodipodi/libnr/nr-pixops.h
172 // but currently none handles de-premultiplication better than the getPixel method
175 static inline int MultiplyAlpha(const int a, const int r)
177 register int temp;
178 temp = (r * a) + 0x80;
179 return ((temp + (temp >> 8)) >> 8);
182 #pragma mark -- Graphics Context Utilities
183 // returns a new rgba bitmap context. you owns the returned context. should be released after
184 NSBitmapImageRep* CreateBitmapContextRGBA(const int width, const int height)
186 return
187 [[NSBitmapImageRep alloc]
188 initWithBitmapDataPlanes:NULL
189 pixelsWide:width
190 pixelsHigh:height
191 bitsPerSample:8
192 samplesPerPixel:4
193 hasAlpha:YES
194 isPlanar:NO
195 colorSpaceName:NSCalibratedRGBColorSpace //may be NSDeviceRGBColorSpace is faster since it matches hardware properties
196 // bitmapFormat: NSAlphaNonpremultipliedBitmapFormat // better for pixel manipulation - does not work on 10.5
197 // bitmapFormat: NSAlphaFirstBitmapFormat
198 bytesPerRow:/*(width * height * 4)*/ 0 // calc word alignment - faster
199 bitsPerPixel:0 // guess it for me ;-)
203 // creates and return an autoreleased NSBitmapImageRep built from an CIImage or an NSImage
204 // if image is nil then it will return a zero filled pixel NSBitmapImageRep
205 NSBitmapImageRep* BitmapContextRGBAWithImage(const int width, const int height, id image)
207 NSGraphicsContext *nsPrevCtx = NULL;
208 NSBitmapImageRep *bitmap = CreateBitmapContextRGBA(width, height);
209 if(!bitmap) {
210 NSLog(@"Error: CreateBitmapContextRGBAWithImage Failed creating valid RGBA Bitmap context");
211 return nil;
214 if(image) {
216 [NSGraphicsContext saveGraphicsState];
218 NSGraphicsContext *nsCtx =
219 [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap];
221 if(!nsCtx) {
222 NSLog(@"Error: CreateBitmapContextRGBAWithImage Failed creating valid NSGraphicsContext with RGBA Bitmap Attributes");
223 [bitmap release];
224 return nil;
227 [NSGraphicsContext setCurrentContext:nsCtx]; // set the RGBA ctx
229 if([image isKindOfClass:[NSImage class]]) {
230 if([image isValid]) {
231 [image drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0]; // draw inside of it
233 else {
234 NSLog(@"Error: invalid image supplied as input to the bitmap context");
235 goto failure;
238 else if([image isKindOfClass:[CIImage class]]) {
239 CGRect extent = [image extent];
240 extent.origin = (CGPoint){0.f, 0.f};
241 [ [nsCtx CIContext] drawImage:image inRect:CGRectMake(0.f, 0.f, (float)width, (float)height) fromRect:extent];
243 else {
244 NSLog(@"Error: Invalid class supplied as input to bitmap context !");
245 goto failure;
248 [NSGraphicsContext restoreGraphicsState];
251 else {
252 memset([bitmap bitmapData], 0, [bitmap bytesPerRow]*[bitmap pixelsHigh]); // better to clear everything
255 return [bitmap autorelease];
257 failure:
258 [bitmap release];
259 if(nsPrevCtx) [NSGraphicsContext setCurrentContext:nsPrevCtx]; // restore previous ctx
260 return nil;
263 #pragma mark -- SCImage Class
265 @implementation SCImage
267 -(id)initWithCIImage:(CIImage*)image extent:(CGRect)extent format:(CIFormat)imageFormat
269 if([super init]) {
271 [self commonInit];
272 _ciFormat = imageFormat;
274 #if SCIMAGE_MAC_OS_10_5
275 _ciimageStore = [[CIImageAccumulator allocWithZone:[self zone]]initWithExtent:extent format:_ciFormat];
276 if(!_ciimageStore) {
277 NSLog(@"SCImage initWithContentsOfURL failed creating valid CIImageAccumulator !");
278 goto failure;
280 [_ciimageStore setImage:image];
281 #else
282 // extent and format cannot be choosen
283 _ciimage = [image retain];
284 #endif
285 _accelerated = true;
286 [self setShouldSyncBitmap:YES];
288 return self;
290 #if SCIMAGE_MAC_OS_10_5
291 failure:
292 #endif
293 [super dealloc];
294 return nil;
297 -(id)initWithNSImage:(NSImage*)image {
298 if([super init]) {
300 // TODO: clean up the initFromURL method...ect... to use this method instead
301 NSSize size;
302 size = [image size];
305 NSImage seems to have an incorrect size depending on the dpi of the image
306 find the largest representation in the nsimage and the set the size according to it
308 int pixelsHigh=0, pixelsWide=0; // should be NSInteger in 10.5 - but stick with an <int> for now
309 NSImageRep *irep;
310 NSEnumerator *e = [[image representations]objectEnumerator];
311 while( (irep = [e nextObject]) ) {
313 do not check ratio - they should be always preserved - just take the max
314 they should always be part of the same rep
316 pixelsHigh = sc_max([irep pixelsHigh], pixelsHigh);
317 pixelsWide = sc_max([irep pixelsWide], pixelsWide);
320 if( size.width < (float)pixelsWide || size.height < (float)pixelsHigh ) {
321 //post("converting : %f %f -> %i %i\n", size.width, size.height, pixelsWide, pixelsHigh);
322 [image setSize:NSMakeSize((float)pixelsWide, (float)pixelsHigh)];
323 size = [image size];
326 _bitmap = BitmapContextRGBAWithImage([image size].width, [image size].height, image);
327 if(!_bitmap)
328 goto failure;
329 [_bitmap retain];
330 [self rebuildNSImageFromBitmapRep];
332 _accelerated = NO;
333 [self commonInit];
334 [self setShouldSyncCIImage:YES];
336 return self;
339 failure:
340 [super dealloc];
341 return nil;
344 -(id)initWithSize:(NSSize)size isAccelerated:(BOOL)yorn format:(CIFormat)imageFormat
346 if([super init]) {
348 _bitmap = BitmapContextRGBAWithImage((int)size.width, (int)size.height, nil);
349 if(!_bitmap)
350 goto failure;
351 [_bitmap retain];
352 [self rebuildNSImageFromBitmapRep];
354 if(yorn) {
355 [self rebuildCIImageFromBitmapRep];
358 _accelerated = yorn;
359 [self commonInit];
361 if(!yorn)
362 [self setShouldSyncCIImage:YES];
364 return self;
367 failure:
368 [super dealloc];
369 return nil;
372 -(id)copy {
373 return [[SCImage alloc]initWithNSImage:[self nsimage]]; // perform a full pixel copy
376 -(id)initWithContentsOfURL:(NSURL*)url isAccelerated:(BOOL)yorn format:(CIFormat)imageFormat
378 if([super init]) {
380 _ciFormat = imageFormat;
381 [self commonInit];
383 if(yorn) {
384 CIImage *ciimage = [[CIImage alloc] initWithContentsOfURL:url options:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:_ciFormat], @"CIFormat"]];
385 #if SCIMAGE_MAC_OS_10_5
386 _ciimageStore = [[CIImageAccumulator allocWithZone:[self zone]]initWithExtent:[ciimage extent] format:_ciFormat];
387 if(!_ciimageStore) {
388 NSLog(@"SCImage initWithContentsOfURL failed creating valid CIImageAccumulator !");
389 [ciimage release];
390 goto failure;
392 [_ciimageStore setImage:ciimage];
393 #else
394 _ciimage = [ciimage retain];
395 #endif
397 else
399 NSImage *nsimage = [[NSImage alloc] initWithContentsOfURL:url];
400 if(!nsimage)
401 goto failure;
403 NSSize size;
404 size = [nsimage size];
407 NSImage seems to have an incorrect size depending on the dpi of the image
408 find the largest representation in the nsimage and the set the size according to it
410 int pixelsHigh=0, pixelsWide=0; // should be NSInteger in 10.5 - but stick with an <int> for 10.4
411 NSImageRep *irep;
412 NSEnumerator *e = [[nsimage representations]objectEnumerator];
413 while( (irep = [e nextObject]) ) {
415 do not check ratio - they should be always preserved - just take the max
416 they should always be part of the same rep
418 pixelsHigh = sc_max([irep pixelsHigh], pixelsHigh);
419 pixelsWide = sc_max([irep pixelsWide], pixelsWide);
422 if( size.width < (float)pixelsWide || size.height < (float)pixelsHigh ) {
423 //post("converting : %f %f -> %i %i\n", size.width, size.height, pixelsWide, pixelsHigh);
424 [nsimage setSize:NSMakeSize((float)pixelsWide, (float)pixelsHigh)];
425 size = [nsimage size];
428 _bitmap = BitmapContextRGBAWithImage( (int)size.width, (int)size.height, nsimage );
429 if(!_bitmap) {
430 [nsimage release];
431 goto failure;
433 [_bitmap retain];
434 [nsimage release];
437 _accelerated = yorn;
439 if(yorn)
440 [self setShouldSyncBitmap:YES];
441 else
442 [self setShouldSyncCIImage:YES];
444 return self;
447 failure:
448 [super dealloc];
449 return nil;
452 // Common initializer for both models
453 -(void)commonInit
455 _inited = NO;
456 _bitmapSynced = _ciimageSynced = YES;
457 _interpolation = NSImageInterpolationDefault;
458 _nsimage = nil;
459 #if SCIMAGE_MAC_OS_10_5
460 _ciimageStore = nil;// CAREFUL: may lead to error / crash if commonInit is called after previous _ciimageStore initialization
461 #else
462 _ciimage = nil;// CAREFUL: may lead to error / crash if commonInit is called after previous _ciimage initialization
463 #endif
464 _wLock = [[NSLock alloc]init];
465 _scales = NO;
466 _prevCtx = NULL;
467 _hints = (SCImageHint)1; // do not optimize drawing when using non-accelerated model - hence using filter will do nothing
468 _inited = YES; // initialization flag
469 _savedCtxState = NO;
470 _cache = NULL;
472 - (void)setDrawingHint:(SCImageHint)hint
474 _hints = hint; //
476 -(void)setScalesWhenResized:(BOOL)yorn
478 _scales = yorn;
479 [[self nsimage]setScalesWhenResized:yorn]; // test for now
481 -(BOOL)scalesWhenResized
483 return _scales;
485 -(void)setSize:(NSSize)s
487 if([self size].width != s.width || [self size].height != s.height)
489 [self lock]; // in case
491 #if SCIMAGE_DEBUG
492 NSLog(@"Resizing Image %f %f", s.width, s.height);
493 #endif
495 BOOL scales = [self scalesWhenResized];
497 NSBitmapImageRep *new_bitmap = CreateBitmapContextRGBA(s.width, s.height);
498 // NSGraphicsContext* prevctx = [NSGraphicsContext currentContext];
500 [NSGraphicsContext saveGraphicsState];
502 NSGraphicsContext *nctx =
504 NSGraphicsContext graphicsContextWithAttributes:
505 [NSDictionary dictionaryWithObject:new_bitmap forKey:NSGraphicsContextDestinationAttributeName]
506 ]; // create new RGBA ctx with bitmap as destination
508 [NSGraphicsContext setCurrentContext:nctx];
509 if([self isAccelerated])
511 if(scales)
512 [[nctx CIContext]drawImage:[self ciimage] inRect:CGRectMake(0.f, 0.f, s.width, s.height) fromRect:[self extent]];
513 else
514 [[nctx CIContext]drawImage:[self ciimage] atPoint:CGPointMake(0.f, 0.f) fromRect:[self extent]];
516 else
518 if(scales)
519 [_bitmap drawInRect:NSMakeRect(0.f, 0.f, s.width, s.height)];
520 else
521 [_bitmap drawAtPoint:NSMakePoint(0.f, 0.f)];
523 // [NSGraphicsContext setCurrentContext:prevctx];
524 [NSGraphicsContext restoreGraphicsState];
526 [_bitmap release];
527 _bitmap = [new_bitmap retain];
529 [self rebuildNSImageFromBitmapRep];
531 if([self isAccelerated])
532 [self rebuildCIImageFromBitmapRep];
534 _ciimageSynced = _bitmapSynced = YES; // everybody's synced
536 [self unlock];
539 -(void)setAccelerated:(BOOL)yorn
541 if(yorn != _accelerated) {
542 [self syncRepresentations]; // sync representations
543 _accelerated = yorn;
546 -(BOOL)isAccelerated
548 return _accelerated;
550 -(BOOL)isSynced
552 return (_bitmapSynced && _ciimageSynced);
554 -(NSImage*)nsimage
556 if(!_nsimage)
557 [self rebuildNSImageFromBitmapRep]; // in case
559 return _nsimage;
561 -(CIImage*)ciimage
563 #if SCIMAGE_MAC_OS_10_5
564 if(!_ciimageStore && _bitmap)
565 [self rebuildCIImageFromBitmapRep];
566 return [_ciimageStore image];
567 #else
568 if(!_ciimage && _bitmap)
569 [self rebuildCIImageFromBitmapRep];
570 return _ciimage;
571 #endif
574 -(NSBitmapImageRep*)bitmapRepresentation
576 #if SCIMAGE_MAC_OS_10_5
577 if(!_bitmap && _ciimageStore)
578 #else
579 if(!_bitmap && _ciimage)
580 #endif
581 [self rebuildBitmapRepFromCIImage];// in case
583 return _bitmap;
585 -(void)rebuildNSImageFromBitmapRep
587 if(!_bitmap) {
588 NSLog(@"Error: SCImage rebuildCIImageFromBitmapRep: No valuable Bitmap Context to rebuild the CIImage");
589 return;
592 if(_nsimage) {
593 [_nsimage release];
594 _nsimage = nil;
596 _nsimage = [[NSImage alloc]initWithSize:NSMakeSize([_bitmap pixelsWide], [_bitmap pixelsHigh])]; // simple store
597 [_nsimage addRepresentation:_bitmap];
599 #if SCIMAGE_DEBUG
600 NSLog(@"SCImage (%p) Recaching NSImage (%p) from BitmapRep (%p)", self, _nsimage, _bitmap);
601 #endif
603 -(void)rebuildCIImageFromBitmapRep
605 if(!_bitmap) {
606 NSLog(@"Error: SCImage rebuildCIImageFromBitmapRep: No valuable Bitmap Context to rebuild the CIImage");
607 return;
610 #if SCIMAGE_MAC_OS_10_5
611 if(_ciimageStore)
612 [_ciimageStore release];
614 CIImage * _ciimage = [[CIImage alloc]initWithBitmapImageRep:[self bitmapRepresentation]];
615 _ciimageStore = [[CIImageAccumulator allocWithZone:[self zone]]initWithExtent:[_ciimage extent] format:_ciFormat];
616 //#endif
617 if(_ciimageStore) {
618 [_ciimageStore setImage:[_ciimage autorelease]];
620 else
621 NSLog(@"Error: SCImage (%p) Failed Creating valid CIImageAccumulator");
622 #else
623 [_ciimage release];
624 _ciimage = [[CIImage alloc]initWithBitmapImageRep:[self bitmapRepresentation]];
625 #endif
627 #if SCIMAGE_DEBUG
628 NSLog(@"SCImage (%p) Recaching CIImage (%p) from BitmapRep (%p)", self, _ciimage, _bitmap);
629 #endif
631 -(void)rebuildBitmapRepFromCIImage
633 // rebuild a BitmapContext from the CIImage
634 // expensive since it may reload it from the GPU.
635 // only if - user wants to manipulate pixels after using filter
636 // - user wants to save data in file
637 if(_bitmap) {
638 [_bitmap release];
639 _bitmap = nil;
641 CIImage *ciimage = [self ciimage];
642 CGRect extent = [ciimage extent];
643 _bitmap = BitmapContextRGBAWithImage((int)extent.size.width, (int)extent.size.height, ciimage); // create a context from CIImage
644 [_bitmap retain]; // retain it since it is an autoreleased one
645 #if SCIMAGE_DEBUG
646 NSLog(@"SCImage (%p) Recaching NSBitmapImageRep (%p) from CIImage (%p)", self, _bitmap, ciimage);
647 #endif
650 -(void)recache
652 // currently does nothing
655 - (NSImageInterpolation)imageInterpolation
657 return _interpolation;
660 - (void)setImageInterpolation:(NSImageInterpolation)interp
662 #if SCIMAGE_DEBUG
663 NSLog(@"SCImage (%p) setImageInterpolation: %i", self, interp);
664 #endif
665 _interpolation = interp;
668 // update all representations from the current used rep
669 -(void)syncRepresentations
671 if([self isSynced])
672 return;
674 if([self isAccelerated]) {
675 [self syncBitmap];
676 }else{
677 [self syncCIImage];
681 -(void)syncBitmap {
682 if(!_bitmapSynced) {
683 #if SCIMAGE_DEBUG
684 NSLog(@"SCImage (%p) syncBitmap", self);
685 #endif
686 [self rebuildBitmapRepFromCIImage];
687 [self rebuildNSImageFromBitmapRep];
688 _bitmapSynced = YES;
692 -(void)syncCIImage {
693 if(!_ciimageSynced) {
694 #if SCIMAGE_DEBUG
695 NSLog(@"SCImage (%p) syncCIImage", self);
696 #endif
697 [self rebuildCIImageFromBitmapRep];
698 _ciimageSynced = YES;
702 - (BOOL)hasChanged {
703 return (![self isSynced] || (_cache == NULL));
706 -(void)setShouldSyncCIImage:(BOOL)yorn {
707 _ciimageSynced = !yorn;
710 -(void)setShouldSyncBitmap:(BOOL)yorn {
711 _bitmapSynced = !yorn;
714 -(int)width
716 return (int)[self size].width;
719 -(int)height
721 return (int)[self size].height;
724 -(CGRect)extent {
725 return [self isAccelerated] ? [[self ciimage]extent] : CGRectMake(0.f, 0.f, (float)[_bitmap pixelsWide], (float)[_bitmap pixelsHigh]);
728 -(NSSize)size {
729 if([self isAccelerated]) {
730 CGSize s = [[self ciimage]extent].size;
731 return NSMakeSize(s.width, s.height);
732 }else{
733 return NSMakeSize([_bitmap pixelsWide], [_bitmap pixelsHigh]);
737 -(unsigned char*)bitmapData
739 if([self isAccelerated])
740 [self syncBitmap];
741 return [[self bitmapRepresentation] bitmapData];
744 -(void)setPixel:(PixelData *)pixel atX:(int)x y:(int)y
746 if([self isAccelerated])
747 [self syncBitmap];
748 [[self bitmapRepresentation] setPixel:pixel atX:(long)x y:(long)y];
749 [self setShouldSyncCIImage:YES];
752 -(void)setColor:(NSColor*)color atX:(unsigned int)x y:(unsigned int)y
754 if([self isAccelerated])
755 [self syncBitmap];
756 [[self bitmapRepresentation] setColor:color atX:x y:y];
757 [self setShouldSyncCIImage:YES];
760 -(void)getPixel:(PixelData *)pixel atX:(int)x y:(int)y
762 if([self isAccelerated])
763 [self syncBitmap];
764 [[self bitmapRepresentation] getPixel:pixel atX:x y:y];
767 -(NSColor*)colorAtX:(unsigned int)x y:(unsigned int)y
769 if([self isAccelerated])
770 [self syncBitmap];
771 return [[self bitmapRepresentation]colorAtX:x y:y];
774 -(void)lock {
775 [_wLock lock];
778 -(void)unlock {
779 [_wLock unlock];
782 -(void)lockFocus
784 [self lock];
785 if([self isAccelerated])
786 [self syncBitmap];
788 //_prevCtx = [NSGraphicsContext currentContext];
790 _savedCtxState = YES;
791 [NSGraphicsContext saveGraphicsState];
793 NSGraphicsContext *nsCtx =
794 [NSGraphicsContext graphicsContextWithAttributes:
795 [NSDictionary dictionaryWithObject:[self bitmapRepresentation] forKey:NSGraphicsContextDestinationAttributeName]
796 ]; // create new RGBA ctx with bitmap as destination
798 if(!nsCtx) {
799 NSLog(@"Error: SCImage lockFocus: Failed setting valid NSGraphicsContext");
800 _prevCtx = NULL;
801 [NSGraphicsContext restoreGraphicsState];
803 if(_interpolation != NSImageInterpolationDefault) {
804 [nsCtx setImageInterpolation:_interpolation];
806 [NSGraphicsContext setCurrentContext:nsCtx]; // set the RGBA ctx
810 -(void)unlockFocus
812 // if(_prevCtx)
813 // [NSGraphicsContext setCurrentContext:_prevCtx];
815 if ( _savedCtxState )
816 [NSGraphicsContext restoreGraphicsState];
818 _savedCtxState = NO;
820 _prevCtx = NULL;
822 [self setShouldSyncCIImage:YES];
823 [self unlock];
826 - (void)drawAtPoint:(NSPoint)point fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(float)delta
828 NSImageInterpolation interp = NSImageInterpolationDefault;
829 NSGraphicsContext *ctx = NULL;
831 ctx = [NSGraphicsContext currentContext];
832 NSAssert(ctx != nil, @"SCImage DrawAtPoint Failed retrieving valid GraphicsContext");
834 if(_interpolation != NSImageInterpolationDefault) {
835 interp = [ctx imageInterpolation];
836 [ctx setImageInterpolation:_interpolation];
839 #if SCIMAGE_DEBUG
840 double past = GetTimeOfDay();
841 #endif
843 if(_accelerated || _hints == SCImageUseGraphicsAccelerationWhenPossible) // Keep CIImage up-to-date
845 [self syncCIImage]; // update if needed
847 if(delta < 1.0f || op != NSCompositeCopy)
848 [[self ciimage]drawAtPoint:point fromRect:srcRect operation:op fraction:delta];
849 else // true CIContext draw
850 [[ctx CIContext]drawImage:[self ciimage] atPoint:*(CGPoint*)&point fromRect:*(CGRect*)&srcRect];
852 #if SCIMAGE_DEBUG
853 NSLog(@"SCImage (%p) CIImage drawAtPoint Called Time To Render: %f", self, GetTimeOfDay() - past);
854 #endif
856 else
858 [self syncBitmap]; // update if needed
860 [[self nsimage]drawAtPoint:point fromRect:srcRect operation:op fraction:delta];
861 #if SCIMAGE_DEBUG
862 NSLog(@"SCImage (%p) NSImage drawAtPoint Called Time To Render: %f", self, GetTimeOfDay() - past);
863 #endif
866 [ctx setImageInterpolation:interp];
869 - (void)drawInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(float)delta
871 NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
872 NSAssert(ctx != nil, @"SCImage DrawAtPoint Failed retrieving valid GraphicsContext");
874 #if SCIMAGE_DEBUG
875 double past = GetTimeOfDay();
876 #endif
878 if(_accelerated)
880 [self syncCIImage]; // update if needed
882 if(delta < 1.0f || op != NSCompositeSourceOver)
883 [[self ciimage]drawInRect:dstRect fromRect:srcRect operation:op fraction:delta];
884 else
885 [[ctx CIContext]drawImage:[self ciimage] inRect:*(CGRect*)&dstRect fromRect:*(CGRect*)&srcRect];
887 #if SCIMAGE_DEBUG
888 NSLog(@"SCImage (%p) CIImage drawInRect Called Time To Render: %f", self, GetTimeOfDay() - past);
889 #endif
891 else
893 [self syncBitmap];
895 NSImageInterpolation interp = NSImageInterpolationDefault;
896 if(_interpolation != NSImageInterpolationDefault) {
897 interp = [ctx imageInterpolation];
898 [ctx setImageInterpolation:_interpolation];
900 [[self nsimage]drawInRect:dstRect fromRect:srcRect operation:op fraction:delta];
902 #if SCIMAGE_DEBUG
903 NSLog(@"SCImage (%p) NSImage drawInRect Called Time To Render: %f", self, GetTimeOfDay() - past);
904 #endif
906 [ctx setImageInterpolation:interp];
911 - (void)drawInSCRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(float)delta
913 NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
914 NSAssert(ctx != nil, @"SCImage DrawAtPoint Failed retrieving valid GraphicsContext");
916 #if SCIMAGE_DEBUG
917 double past = GetTimeOfDay();
918 #endif
920 srcRect.origin.y = [self size].height - srcRect.origin.y - srcRect.size.height;
922 if(_accelerated)
924 [self syncCIImage]; // update if needed
926 CIImage* timage;
927 NSAffineTransform *transform = [NSAffineTransform transform];
928 [transform translateXBy:0 yBy:[self extent].size.height];
929 [transform scaleXBy:1.0 yBy:-1.0];
931 CIFilter* fmat = [CIFilter filterWithName:@"CIAffineTransform"];
932 if(!fmat) {
933 NSLog(@"SCImageBackground Failed getting valid CIfilter !");
934 return;
937 [fmat setDefaults];
938 [fmat setValue: [self ciimage] forKey: @"inputImage"];
939 [fmat setValue: transform forKey: @"inputTransform"];
940 timage = [fmat valueForKey:@"outputImage"];
941 if(op != NSCompositeSourceOver && op != NSCompositeCopy)
943 [timage drawInRect:dstRect fromRect:srcRect operation:op fraction:delta];
945 else
947 if(delta < 1.f) {
948 CIFilter* fcol = [CIFilter filterWithName:@"CIColorMatrix"];
949 [fcol setDefaults];
950 [fcol setValue: timage forKey:@"inputImage"];
951 [fcol setValue: [CIVector vectorWithX:0.0 Y:0.0 Z:0.0 W:delta] forKey:@"inputAVector"];
952 timage = [fcol valueForKey:@"outputImage"];
955 [[ctx CIContext]drawImage:timage inRect:*(CGRect*)&dstRect fromRect:*(CGRect*)&srcRect];
958 #if SCIMAGE_DEBUG
959 NSLog(@"SCImage (%p) CIImage drawInRect Called Time To Render: %f", self, GetTimeOfDay() - past);
960 #endif
962 else
964 CGContextRef cgctx = (CGContextRef)[[NSGraphicsContext currentContext]graphicsPort];
965 CGContextSaveGState(cgctx);
967 NSImageInterpolation interp = NSImageInterpolationDefault;
968 if(_interpolation != NSImageInterpolationDefault) {
969 interp = [ctx imageInterpolation];
970 [ctx setImageInterpolation:_interpolation];
973 [self syncBitmap];
975 CGContextTranslateCTM(cgctx, dstRect.origin.x, dstRect.origin.y + dstRect.size.height);
976 CGContextScaleCTM(cgctx, 1, -1.0f);
977 dstRect.origin.y = dstRect.origin.x = 0.f;
978 [[self nsimage]drawInRect:dstRect fromRect:srcRect operation:op fraction:delta];
980 #if SCIMAGE_DEBUG
981 NSLog(@"SCImage (%p) NSImage drawInRect Called Time To Render: %f", self, GetTimeOfDay() - past);
982 #endif
984 [ctx setImageInterpolation:interp]; // needed before ?? to test
985 CGContextRestoreGState(cgctx);
989 - (void)tileInSCRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(float)delta
992 CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext]graphicsPort];
993 if(!cgc)
994 return;
996 CGContextSaveGState(cgc);
997 CGContextClipToRect(cgc, *(CGRect*)&dstRect);
999 int numCols = (int)ceilf(sc_max(1, (dstRect.size.width / srcRect.size.width) + 0.5));
1000 int numRows = (int)ceilf(sc_max(1, (dstRect.size.height / srcRect.size.height) + 0.5));
1002 int y,x;
1003 srcRect.origin.y = [self size].height - srcRect.origin.y - srcRect.size.height;
1004 CGPoint where = CGPointMake(dstRect.origin.x, dstRect.origin.y);
1005 CGLayerRef cgLayer;
1006 if(_accelerated)
1008 [self syncCIImage];
1009 cgLayer = CGLayerCreateWithContext(cgc, *(CGSize*)&srcRect.size, NULL);
1010 CGContextRef lcg = CGLayerGetContext(cgLayer);
1011 CGContextTranslateCTM(lcg, 0.f, srcRect.size.height);
1012 CGContextScaleCTM(lcg, 1, -1.0f);
1013 CIContext *cictx = [[CIContext contextWithCGContext:lcg options:nil]retain];
1014 [cictx drawImage:[self ciimage] inRect:CGRectMake(0.f, 0.f, srcRect.size.width, srcRect.size.height) fromRect:*(CGRect*)&srcRect];
1015 [cictx release];
1017 else
1019 [self syncBitmap];
1020 cgLayer = CGLayerCreateWithContext(cgc, *(CGSize*)&srcRect.size, NULL);
1021 CGContextRef lcg = CGLayerGetContext(cgLayer);
1022 [NSGraphicsContext saveGraphicsState];
1023 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:lcg flipped:NO]];
1024 CGContextTranslateCTM(lcg, 0.f, srcRect.size.height);
1025 CGContextScaleCTM(lcg, 1, -1.0f);
1026 [[self nsimage]drawInRect:NSMakeRect(0.f, 0.f, srcRect.size.width, srcRect.size.height) fromRect:*(NSRect*)&srcRect operation:NSCompositeCopy fraction:delta];
1027 [NSGraphicsContext restoreGraphicsState];
1029 _cache = (void*)cgLayer;
1031 #if SCIMAGE_DEBUG
1032 double past = GetTimeOfDay();
1033 #endif
1035 if(cgLayer)
1037 if(fraction < 1.0)
1038 CGContextSetAlpha(cgc, delta);
1040 for(y=0; y < numRows; ++y)
1042 where.x = dstRect.origin.x;
1043 for(x=0; x < numCols; ++x)
1045 CGContextDrawLayerAtPoint(cgc, where, cgLayer);
1046 where.x += srcRect.size.width;
1048 where.y += srcRect.size.height;
1052 #if SCIMAGE_DEBUG
1053 NSLog(@"SCImage (%p) drawInRect Called Time To Render: %f", self, GetTimeOfDay() - past);
1054 #endif
1055 CGLayerRelease(cgLayer);
1056 CGContextRestoreGState(cgc);
1059 - (void)applyFilter:(CIFilter*)filter
1061 if(![self isAccelerated])
1062 [self syncCIImage]; // update ciimage rep
1064 if([[filter inputKeys] containsObject:@"inputImage"]) {
1065 [filter setValue:[self ciimage] forKey:@"inputImage"];
1068 CIImage *result = [filter valueForKey:@"outputImage"];
1069 if(!result) {
1070 NSLog(@"Failed Appying filter to image !");
1071 return;
1074 #if SCIMAGE_MAC_OS_10_5
1075 [_ciimageStore setImage:result];
1076 #else
1077 [result retain];
1078 [_ciimage release];
1079 _ciimage = result;
1080 #endif
1082 [self setShouldSyncBitmap:YES]; // invalidates bitmap
1085 -(SCImage*)imageFilteredWith:(CIFilter*)filter
1087 if(![self isAccelerated])
1088 [self syncCIImage];
1090 [filter setValue:[self ciimage] forKey:@"inputImage"];
1091 CIImage *result = [filter valueForKey:@"outputImage"];
1093 if(!result) {
1094 NSLog(@"Failed Appying filter to image !");
1095 return nil;
1098 #if SCIMAGE_MAC_OS_10_5
1099 SCImage *newSCImage = [[SCImage alloc]initWithCIImage:result extent:[_ciimageStore extent] format:_ciFormat];
1100 #else
1101 SCImage *newSCImage = [[SCImage alloc]initWithCIImage:result extent:[_ciimage extent] format:_ciFormat];
1102 #endif
1104 return [newSCImage autorelease];
1107 -(CIFormat)format
1109 return _ciFormat;
1112 #if SCIMAGE_MAC_OS_10_5
1113 -(CIImageAccumulator*)accumulator {
1114 return _ciimageStore;
1116 #endif
1118 -(void)dealloc {
1120 #if SCIMAGE_DEBUG
1121 NSLog(@"SCImage (%p) Dealloc", self);
1122 #endif
1124 [_wLock release];
1125 #if SCIMAGE_MAC_OS_10_5
1126 if(_ciimageStore) [_ciimageStore release];
1127 #else
1128 if(_ciimage) [_ciimage release];
1129 #endif
1130 if(_bitmap) [_bitmap release];
1131 if(_nsimage) [_nsimage release];
1132 [super dealloc];
1134 @end
1136 #pragma mark -- SCImage Primitive Set
1137 //----------------------------------------------------------------------------------------------------
1138 // SCImage primitive set
1140 int prSCImage_New(struct VMGlobals *g, int numArgsPushed);
1141 int prSCImage_New(struct VMGlobals *g, int numArgsPushed)
1143 if (!g->canCallOS) return errCantCallOS;
1145 PyrSlot *receiver = g->sp - 3; // instance of SCImage
1146 PyrSlot *a = g->sp - 2; // width
1147 PyrSlot *b = g->sp - 1; // height
1148 PyrSlot *c = g->sp; // graphics acceleration
1150 float width, height;
1151 int err = slotFloatVal(a, &width);
1152 if (err) return err;
1153 err = slotFloatVal(b, &height);
1154 if (err) return err;
1157 SCImage* image = [[SCImage alloc]initWithSize: NSMakeSize(width, height) isAccelerated:IsTrue(c) format:kCIFormatARGB8];
1158 if(!image){
1159 NSLog(@"prSCImage_New Failed Creating valid SCImage !");
1160 return errFailed;
1163 SetPtr(slotRawObject(receiver)->slots + 0, image);
1164 SetFloat(slotRawObject(receiver)->slots + 1, width);
1165 SetFloat(slotRawObject(receiver)->slots + 2, height);
1166 InstallFinalizer(g, slotRawObject(receiver), 9, FinalizeSCImageObject);
1168 return errNone;
1171 int prSCImage_NewFromURL(struct VMGlobals *g, int numArgsPushed);
1172 int prSCImage_NewFromURL(struct VMGlobals *g, int numArgsPushed)
1174 if (!g->canCallOS) return errCantCallOS;
1176 PyrSlot *receiver = g->sp - 1; // instance of SCImage
1177 PyrSlot *a = g->sp; // URL
1179 int err;
1180 char *urlpath=0;
1181 err = allocSlotStrVal(a, &urlpath);
1182 if (err) return err;
1184 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_4
1185 NSString *ns_urlpath = [[[NSString alloc] initWithCString:urlpath length:strlen(urlpath)]autorelease]; // Deprecated >= 10.4
1186 #else
1187 NSString *ns_urlpath = [NSString stringWithCString:urlpath encoding:NSASCIIStringEncoding];
1188 #endif
1189 if(!ns_urlpath) {
1190 NSLog(@"Failed Creating valid NSURL from string: %s", urlpath);
1191 return errFailed;
1193 delete [] urlpath;
1195 SCImage* image = [[SCImage alloc]initWithContentsOfURL:[NSURL URLWithString:ns_urlpath] isAccelerated:NO format:kCIFormatARGB8]; // by default for now set to NO
1196 if(!image) {
1197 NSLog(@"prSCImage_NewFromURL Failed Creating valid SCImage !");
1198 return errFailed;
1201 SetPtr(slotRawObject(receiver)->slots + 0, image);
1202 SetFloat(slotRawObject(receiver)->slots + 1, (float)[image width]); // using size.width / size.height can return non integral number (ex: 499.996 instead of 499)
1203 SetFloat(slotRawObject(receiver)->slots + 2, (float)[image height]); // better to stick with bitmap properties to have true pixel size
1204 InstallFinalizer(g, slotRawObject(receiver), 9, FinalizeSCImageObject);
1206 return errNone;
1209 int prSCImage_WriteToFile(struct VMGlobals *g, int numArgsPushed);
1210 int prSCImage_WriteToFile(struct VMGlobals *g, int numArgsPushed)
1212 if (!g->canCallOS) return errCantCallOS;
1214 PyrSlot *receiver = g->sp - 2; // instance of SCImage
1215 PyrSlot *a = g->sp - 1; // path
1216 PyrSlot *b = g->sp; // image_type
1218 int err;
1219 char *path=0;
1220 err = allocSlotStrVal(a, &path);
1221 if (err) return err;
1223 int image_type;
1224 err = slotIntVal(b, &image_type);
1225 if (err) return err;
1227 NSString *StringPath = [[NSString alloc] initWithCString:path length:strlen(path)];
1228 delete [] path;
1230 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1232 if([image isAccelerated]) {
1233 [image syncBitmap]; // force sync of the bitmap
1235 NSData *data = [[image bitmapRepresentation] representationUsingType:(NSBitmapImageFileType)image_type properties:nil];
1236 BOOL result = [data writeToFile:StringPath atomically:YES];
1238 [StringPath release];
1240 SetBool(receiver, result ? true : false); // inform the user if save is ok
1242 return errNone;
1245 int prSCImage_interpolation(struct VMGlobals *g, int numArgsPushed);
1246 int prSCImage_interpolation(struct VMGlobals *g, int numArgsPushed)
1248 if (!g->canCallOS) return errCantCallOS;
1250 PyrSlot *receiver = g->sp; // instance of SCImage
1251 SCImage *image = (SCImage *)slotRawPtr(&slotRawObject(receiver)->slots[0]);
1252 if(!image)
1253 return errNone;
1255 SetInt(receiver, [image imageInterpolation]); //
1256 return errNone;
1259 int prSCImage_setInterpolation(struct VMGlobals *g, int numArgsPushed);
1260 int prSCImage_setInterpolation(struct VMGlobals *g, int numArgsPushed)
1262 if (!g->canCallOS) return errCantCallOS;
1264 PyrSlot *receiver = g->sp - 1; // SCImage
1265 PyrSlot *a = g->sp; // interpolation
1267 int interp = NSImageInterpolationDefault;
1269 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1270 if(!image)
1271 return errNone;
1273 if(slotIntVal(a, &interp) != errNone)
1274 return errFailed;
1276 [image setImageInterpolation:(NSImageInterpolation)interp];
1278 return errNone;
1281 int prSCImage_Free(struct VMGlobals *g, int numArgsPushed);
1282 int prSCImage_Free(struct VMGlobals *g, int numArgsPushed)
1284 if (!g->canCallOS) return errCantCallOS;
1286 PyrSlot *receiver = g->sp; // instance of SCImage
1287 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1288 if(!image)
1289 return errNone;
1291 #if SCIMAGE_DEBUG
1292 NSLog(@"Releasing SCImage (%p)", image);
1293 #endif
1294 [image release];
1296 SetNil(slotRawObject(receiver)->slots + 0); // ensure slot is NIL
1297 SetNil(slotRawObject(receiver)->slots + 1); // not only the pointer
1298 SetNil(slotRawObject(receiver)->slots + 2);
1300 return errNone;
1303 int prSCImage_sync(struct VMGlobals *g, int numArgsPushed);
1304 int prSCImage_sync(struct VMGlobals *g, int numArgsPushed)
1306 if (!g->canCallOS) return errCantCallOS;
1308 PyrSlot *receiver = g->sp; // instance of SCImage
1309 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1310 [image syncRepresentations]; // force sync
1312 #if SCIMAGE_DEBUG
1313 NSLog(@"SCImage (%p) Force Sync", image);
1314 #endif
1315 return errNone;
1318 int prSCImage_setSize(struct VMGlobals *g, int numArgsPushed);
1319 int prSCImage_setSize(struct VMGlobals *g, int numArgsPushed)
1321 if (!g->canCallOS) return errCantCallOS;
1323 PyrSlot *receiver = g->sp - 2; // instance of SCImage
1324 PyrSlot *a = g->sp - 1; // width
1325 PyrSlot *b = g->sp; // height
1327 int err;
1328 float width;
1329 err = slotFloatVal(a, &width);
1330 if (err) return err;
1332 float height;
1333 err = slotFloatVal(b, &height);
1334 if (err) return err;
1336 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1337 [image setSize: NSMakeSize(width, height)];
1339 SetFloat(slotRawObject(receiver)->slots + 1, width);
1340 SetFloat(slotRawObject(receiver)->slots + 2, height);
1342 return errNone;
1345 int prSCImage_setScalesWhenResized(struct VMGlobals *g, int numArgsPushed);
1346 int prSCImage_setScalesWhenResized(struct VMGlobals *g, int numArgsPushed)
1348 if (!g->canCallOS) return errCantCallOS;
1350 PyrSlot *receiver = g->sp - 1; // instance of SCImage
1351 PyrSlot *a = g->sp; // bool flag
1353 BOOL flag = IsTrue(a);
1354 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1355 [image setScalesWhenResized: flag];
1357 return errNone;
1360 int prSCImage_scalesWhenResized(struct VMGlobals *g, int numArgsPushed);
1361 int prSCImage_scalesWhenResized(struct VMGlobals *g, int numArgsPushed)
1363 if (!g->canCallOS) return errCantCallOS;
1365 PyrSlot *receiver = g->sp; // instance of SCImage
1367 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1368 SetBool(receiver, [image scalesWhenResized]);
1369 return errNone;
1372 int prSCImage_DrawAtPoint(struct VMGlobals *g, int numArgsPushed);
1373 int prSCImage_DrawAtPoint(struct VMGlobals *g, int numArgsPushed)
1375 if (!g->canCallOS) return errCantCallOS;
1377 PyrSlot *d = g->sp; // fraction
1378 PyrSlot *c = g->sp - 1; // compositing operation
1379 PyrSlot *b = g->sp - 2; // fromRect
1380 PyrSlot *a = g->sp - 3; // point
1381 PyrSlot *receiver = g->sp - 4; // instance of SCImage
1383 int err;
1384 NSPoint nsPoint;
1386 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1387 if(!image)
1388 return errNone; // just do nothing
1390 if(![image ciimage])
1392 NSLog(@"SCImage CIImage is nil !");
1393 return errFailed;
1396 err = slotGetPoint(a, &nsPoint);
1397 if (err) return err;
1399 NSRect rect;
1400 if (IsNil(b)) {
1401 rect = NSMakeRect(0.0,0.0,[image size].width,[image size].height);
1402 } else {
1403 err = slotGetNSRect(b, &rect);
1404 if (err) return err;
1407 int co;
1408 err = slotIntVal(c, &co);
1409 if (err) return err;
1411 float fraction;
1412 err = slotFloatVal(d, &fraction);
1413 if (err) return err;
1415 // [image drawInSCRect:NSMakeRect(nsPoint.x, nsPoint.y, [image size].width, [image size].height) fromRect:rect operation:(NSCompositingOperation)co fraction:fraction];
1416 [image drawInSCRect:NSMakeRect(nsPoint.x, nsPoint.y, rect.size.width, rect.size.height) fromRect:rect operation:(NSCompositingOperation)co fraction:fraction];
1418 return errNone;
1421 int prSCImage_DrawInRect(struct VMGlobals *g, int numArgsPushed);
1422 int prSCImage_DrawInRect(struct VMGlobals *g, int numArgsPushed)
1424 if (!g->canCallOS) return errCantCallOS;
1426 PyrSlot *d = g->sp; // fraction
1427 PyrSlot *c = g->sp - 1; // compositing operation
1428 PyrSlot *b = g->sp - 2; // fromRect
1429 PyrSlot *a = g->sp - 3; // rect
1430 PyrSlot *receiver = g->sp - 4; // instance of SCImage
1432 int err;
1433 NSRect rect, fromRect;
1435 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1436 if(!image) {
1437 return errNone;
1440 err = slotGetNSRect(a, &rect);
1441 if (err) return err;
1443 if (IsNil(b)) {
1444 fromRect = NSMakeRect(0.0,0.0,[image size].width,[image size].height); // CIImage does not support NSZeroRect as argument
1445 } else {
1446 err = slotGetNSRect(b, &fromRect);
1447 if (err) return err;
1450 int co;
1451 err = slotIntVal(c, &co);
1452 if (err) return err;
1454 float fraction;
1455 err = slotFloatVal(d, &fraction);
1456 if (err) return err;
1458 [image drawInSCRect:rect fromRect:fromRect operation:(NSCompositingOperation)co fraction:fraction];
1460 return errNone;
1463 int prSCImage_TileInRect(struct VMGlobals *g, int numArgsPushed);
1464 int prSCImage_TileInRect(struct VMGlobals *g, int numArgsPushed)
1466 if (!g->canCallOS) return errCantCallOS;
1468 PyrSlot *d = g->sp; // fraction
1469 PyrSlot *c = g->sp - 1; // compositing operation
1470 PyrSlot *b = g->sp - 2; // fromRect
1471 PyrSlot *a = g->sp - 3; // rect
1472 PyrSlot *receiver = g->sp - 4; // instance of SCImage
1474 NSRect nsFromRect, nsRect;
1475 int err, co;
1476 float fraction;
1477 SCImage *image;
1479 if(IsNil(slotRawObject(receiver)->slots+0))
1480 return errNone;
1482 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1483 if(!image) {
1484 return errNone;
1487 err = slotGetNSRect(a, &nsRect);
1488 if (err) return err;
1490 err = slotGetNSRect(b, &nsFromRect);
1491 if (err) return err;
1493 err = slotFloatVal(d, &fraction);
1494 if (err) return err;
1496 err = slotIntVal(c, &co);
1497 if (err) return err;
1499 [image tileInSCRect:nsRect fromRect:nsFromRect operation:(NSCompositingOperation)co fraction:fraction];
1501 return errNone;
1503 int prSCImage_lockFocus(struct VMGlobals *g, int numArgsPushed);
1504 int prSCImage_lockFocus(struct VMGlobals *g, int numArgsPushed)
1506 if (!g->canCallOS) return errCantCallOS;
1508 PyrSlot *receiver = g->sp; // instance of SCImage
1509 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1510 // here we do not use the set flipped because it will create problem
1511 // if we draw on top of an already image loaded with URL and write it after
1512 // better to S(T(ctx)) directly so everything is fine now
1513 // and we can write it with no problem
1514 [image lockFocus];
1515 CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext]graphicsPort];
1516 CGContextSaveGState(ctx);
1517 CGContextTranslateCTM(ctx, 0, [image size].height);
1518 CGContextScaleCTM(ctx, 1, -1.0f);
1520 return errNone;
1523 int prSCImage_unlockFocus(struct VMGlobals *g, int numArgsPushed);
1524 int prSCImage_unlockFocus(struct VMGlobals *g, int numArgsPushed)
1526 if (!g->canCallOS) return errCantCallOS;
1528 PyrSlot *receiver = g->sp; // instance of SCImage
1529 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1530 // revert the coordinate system
1531 CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext]graphicsPort];
1532 CGContextRestoreGState(ctx);
1534 [image unlockFocus];
1536 return errNone;
1539 int prSCImage_recache(struct VMGlobals *g, int numArgsPushed);
1540 int prSCImage_recache(struct VMGlobals *g, int numArgsPushed)
1542 if (!g->canCallOS) return errCantCallOS;
1544 PyrSlot *receiver = g->sp; // instance of SCImage
1546 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1547 [image recache];
1548 return errNone;
1551 // returns a premultipled alpha pixel as an uint32
1552 int prSCImage_pixelAt(struct VMGlobals *g, int numArgsPushed);
1553 int prSCImage_pixelAt(struct VMGlobals *g, int numArgsPushed)
1555 if (!g->canCallOS) return errCantCallOS;
1557 PyrSlot *receiver = g->sp - 2; // instance of SCImage
1558 PyrSlot *xSlot = g->sp - 1;
1559 PyrSlot *ySlot = g->sp;
1561 int x, y, err;
1562 SCImage *image;
1563 PixelData pixelAsArray[4] = {0, 0, 0, 0};
1565 err = errNone;
1567 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1568 if(!image) return errNone;
1570 err = slotIntVal(xSlot, &x);
1571 if(err) return err;
1573 err = slotIntVal(ySlot, &y);
1574 if(err) return err;
1576 [image setAccelerated:NO];
1577 [image getPixel:pixelAsArray atX:x y:y];
1578 //SetInt(receiver, ((pixelAsArray[0] << 24) | (pixelAsArray[1] << 16) | (pixelAsArray[2] << 8) | pixelAsArray[3]));
1579 if( pixelAsArray[3] == 0 ) { // avoid EXC_ARITHMETIC
1580 SetInt(receiver,
1581 (pixelAsArray[0] << 24) |
1582 (pixelAsArray[1] << 16) |
1583 (pixelAsArray[2] << 8)
1585 }else{
1586 SetInt(receiver,
1587 (COLOR_UNSCALE( pixelAsArray[0], pixelAsArray[3] ) << 24) |
1588 (COLOR_UNSCALE( pixelAsArray[1], pixelAsArray[3] ) << 16) |
1589 (COLOR_UNSCALE( pixelAsArray[2], pixelAsArray[3] ) << 8) |
1590 pixelAsArray[3]
1593 return errNone;
1596 int prSCImage_setPixelAt(struct VMGlobals *g, int numArgsPushed);
1597 int prSCImage_setPixelAt(struct VMGlobals *g, int numArgsPushed)
1599 if (!g->canCallOS) return errCantCallOS;
1601 PyrSlot *receiver = g->sp - 3; // instance of SCImage
1602 PyrSlot *pixelSlot = g->sp - 2;
1603 PyrSlot *xSlot = g->sp - 1;
1604 PyrSlot *ySlot = g->sp;
1606 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1607 if(!image)
1608 return errNone;
1610 int x, y, err;
1611 uint32 pixel;
1612 PixelData pixelData[4]={0, 0, 0, 0};
1614 err = slotIntVal(pixelSlot, (int*)&pixel);
1615 if(err) return err;
1617 err = slotIntVal(xSlot, &x);
1618 if(err) return err;
1620 err = slotIntVal(ySlot, &y);
1621 if(err) return err;
1623 [image setAccelerated:NO];
1625 pixelData[0] = COLOR_SCALE(PIXEL_RED(pixel), PIXEL_ALPHA(pixel));
1626 pixelData[1] = COLOR_SCALE(PIXEL_GREEN(pixel), PIXEL_ALPHA(pixel));
1627 pixelData[2] = COLOR_SCALE(PIXEL_BLUE(pixel), PIXEL_ALPHA(pixel));
1628 pixelData[3] = PIXEL_ALPHA(pixel);
1630 [image setPixel:pixelData atX:x y:y];
1632 return errNone;
1635 int prSCImage_setColorAt(struct VMGlobals *g, int numArgsPushed);
1636 int prSCImage_setColorAt(struct VMGlobals *g, int numArgsPushed)
1638 if (!g->canCallOS) return errCantCallOS;
1640 PyrSlot *receiver = g->sp - 3; // instance of SCImage
1641 PyrSlot *colorSlot = g->sp - 2;
1642 PyrSlot *xSlot = g->sp - 1;
1643 PyrSlot *ySlot = g->sp;
1645 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1646 if(!image)
1647 return errNone;
1649 [image setAccelerated:NO];
1651 int x, y, err;
1652 SCColor color;
1653 NSSize size = [image size];
1655 err = slotColorVal(colorSlot, &color);
1656 if(err) return err;
1658 err = slotIntVal(xSlot, &x);
1659 if(err) return err;
1661 err = slotIntVal(ySlot, &y);
1662 if(err) return err;
1664 [image
1665 setColor:[NSColor colorWithCalibratedRed:color.red green:color.green blue:color.blue alpha:color.alpha]
1666 atX:x
1670 return errNone;
1673 int prSCImage_getColorAt(struct VMGlobals *g, int numArgsPushed);
1674 int prSCImage_getColorAt(struct VMGlobals *g, int numArgsPushed)
1676 if (!g->canCallOS) return errCantCallOS;
1678 PyrSlot *receiver = g->sp - 2; // instance of SCImage
1679 PyrSlot *xSlot = g->sp - 1;
1680 PyrSlot *ySlot = g->sp;
1682 NSColor *nsColor;
1683 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1684 if(!image)
1685 return errNone;
1687 int x, y, err;
1689 err = slotIntVal(xSlot, &x);
1690 if(err) return err;
1692 err = slotIntVal(ySlot, &y);
1693 if(err) return err;
1695 [image setAccelerated:NO];
1697 nsColor = [image
1698 colorAtX:x
1702 if(!nsColor)
1703 SetNil(receiver);
1704 else {
1705 nsColor = [nsColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
1706 PyrObject *color = instantiateObject(g->gc, s_color->u.classobj, 0, false, true);
1707 PyrSlot *slots = color->slots;
1708 SetFloat(slots+0, [nsColor redComponent]);
1709 SetFloat(slots+1, [nsColor greenComponent]);
1710 SetFloat(slots+2, [nsColor blueComponent]);
1711 SetFloat(slots+3, [nsColor alphaComponent]);
1712 SetObject(receiver, color);
1715 return errNone;
1718 int prSCImage_loadPixels(struct VMGlobals *g, int numArgsPushed);
1719 int prSCImage_loadPixels(struct VMGlobals *g, int numArgsPushed)
1721 if (!g->canCallOS) return errCantCallOS;
1723 PyrSlot *receiver = g->sp - 3;
1724 PyrSlot *pixelSlot = g->sp - 2;
1725 PyrSlot *regionSlot= g->sp - 1;
1726 PyrClass *pyrclass;
1728 uint32 *pixelsData;
1729 NSBitmapImageRep *bitmap;
1730 SCImage *image;
1731 int height, width, y=0, x=0, startIndex=0;
1732 NSSize size;
1733 NSRect region;
1735 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1736 if(!image){
1737 SetNil(receiver);
1738 return errNone;
1740 size = [image size];
1742 pyrclass = slotRawObject(pixelSlot)->classptr;
1743 if(pyrclass != class_int32array) {
1744 NSLog(@"prSCImage_loadPixels array argument is not a PyrInt32Array !");
1745 return errFailed;
1748 slotIntVal(g->sp, &startIndex);
1749 if(startIndex < 0)
1750 return errIndexOutOfRange;
1752 PyrInt32Array* allocatedPixelArray = (PyrInt32Array*)slotRawObject(pixelSlot);
1754 bitmap = [image bitmapRepresentation];
1756 if(IsNil(regionSlot)) {
1757 region = NSIntegralRect(NSMakeRect(0.f, 0.f, [bitmap pixelsWide], [bitmap pixelsHigh]));
1758 }else{
1759 if(slotGetNSRect(regionSlot, &region) != errNone)
1760 return errWrongType;
1761 region = NSIntegralRect(region);
1762 if( region.origin.x < 0 || region.origin.y < 0 || ((region.origin.x + region.size.width) > [bitmap pixelsWide]) || ((region.origin.y + region.size.height) > [bitmap pixelsHigh]) )
1763 return errIndexOutOfRange;
1766 height = region.size.height;
1767 width = region.size.width;
1768 pixelsData = (uint32*)allocatedPixelArray->i + startIndex;
1769 PixelData pixel[4]={0, 0, 0, 0};
1771 if((allocatedPixelArray->size - startIndex) < ( height * width /** 4*/ ) ) {
1772 post("Error: pixel array is not of good size ! should be %i bytes long\n", height * width /** 4*/);
1773 return errIndexOutOfRange;
1776 [image lock];
1777 unsigned char alpha;
1778 height = height + region.origin.y; //padding h and w to meet origin
1779 width = width + region.origin.x;
1780 for(y=(int)region.origin.y; y < height; y++) {
1781 for(x=(int)region.origin.x; x < width; x++) {
1783 // most of the unscale routines may be not as precise as we need
1784 // especially when dealing with CIImage <-> NSImage conversion...
1785 // it should be better to use vImage framework directly !
1787 [bitmap getPixel:pixel atX:x y:y];
1788 if(alpha > 0) {
1789 SET_PIXEL_RED ( *pixelsData, COLOR_UNSCALE(pixel[0], pixel[3]) );
1790 SET_PIXEL_GREEN( *pixelsData, COLOR_UNSCALE(pixel[1], pixel[3]) );
1791 SET_PIXEL_BLUE ( *pixelsData, COLOR_UNSCALE(pixel[2], pixel[3]) );
1792 SET_PIXEL_ALPHA( *pixelsData, pixel[3]);
1793 }else{
1794 SET_PIXEL_RED ( *pixelsData, pixel[0] );
1795 SET_PIXEL_GREEN( *pixelsData, pixel[1] );
1796 SET_PIXEL_BLUE ( *pixelsData, pixel[2] );
1797 SET_PIXEL_ALPHA( *pixelsData, pixel[3] );
1799 ++pixelsData;
1801 //bitmapData += bytesLeft;
1803 [image unlock];
1805 return errNone;
1808 int prSCImage_updatePixels(struct VMGlobals *g, int numArgsPushed);
1809 int prSCImage_updatePixels(struct VMGlobals *g, int numArgsPushed)
1811 if (!g->canCallOS) return errCantCallOS;
1813 PyrSlot *receiver = g->sp - 2;
1814 PyrSlot *pixelSlot = g->sp - 1;
1815 SCImage *image;
1817 NSBitmapImageRep *bitmap;
1818 int width, height, bytesPerRow, bytesLeft, startIndex=0, y=0, x=0;
1819 unsigned char *bitmapData;
1820 uint32 *pixelsData;
1821 PyrInt32Array *pixels;
1822 PyrClass *pyrclass;
1824 if(IsNil(pixelSlot))
1825 return errNone;
1827 slotIntVal(g->sp, &startIndex);
1828 if(startIndex < 0)
1829 return errIndexOutOfRange;
1831 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1832 if(!image){
1833 SetNil(receiver);
1834 return errNone;
1837 pyrclass = slotRawObject(pixelSlot)->classptr;
1838 if(pyrclass != class_int32array) {
1839 NSLog(@"prSCImage_loadPixels array argument is not a PyrInt32Array !");
1840 return errFailed;
1843 pixels = (PyrInt32Array*)slotRawObject(pixelSlot);
1845 bitmap = [image bitmapRepresentation];
1846 if(!bitmap) {
1847 NSLog(@"prSCImage_updatePixels failed retrieving valid NSBitmapImageRep...");
1848 return errFailed;
1851 width = [bitmap pixelsWide]; // RGBA
1852 height = [bitmap pixelsHigh];
1853 bytesPerRow = [bitmap bytesPerRow];
1854 bitmapData = [bitmap bitmapData];
1855 pixelsData = (uint32*)pixels->i + startIndex;
1856 bytesLeft = bytesPerRow - (width*4);
1858 if( (pixels->size - startIndex) < (int)(width*height /* *4 */)) {
1859 post("Error: pixel array is not of good size ! should be %i bytes long from index: %i\n", (int)(width*height/**4*/), startIndex);
1860 return errIndexOutOfRange;
1863 [image lock];
1864 unsigned char alpha;
1865 for(y=0; y < height; ++y) {
1866 for(x=0; x < width; ++x) {
1867 alpha = PIXEL_ALPHA(*pixelsData);
1868 *bitmapData++ = COLOR_SCALE(PIXEL_RED(*pixelsData), alpha);
1869 *bitmapData++ = COLOR_SCALE(PIXEL_GREEN(*pixelsData), alpha);
1870 *bitmapData++ = COLOR_SCALE(PIXEL_BLUE(*pixelsData), alpha);
1871 *bitmapData++ = alpha;
1872 ++pixelsData;
1874 bitmapData += bytesLeft;
1876 [image unlock];
1878 [image setShouldSyncCIImage:YES];
1880 return errNone;
1884 int prSCImage_updatePixelsInRect(struct VMGlobals *g, int numArgsPushed);
1885 int prSCImage_updatePixelsInRect(struct VMGlobals *g, int numArgsPushed)
1887 if (!g->canCallOS) return errCantCallOS;
1889 PyrSlot *receiver = g->sp - 3;
1890 PyrSlot *pixelSlot = g->sp - 2;
1891 PyrSlot *rectSlot = g->sp - 1;
1892 SCImage *image;
1894 NSBitmapImageRep *bitmap;
1895 int width, height, bytesPerRow, bytesLeft, y=0, x=0, startIndex=0, err;
1896 unsigned char *bitmapData;
1897 uint32 *pixelsData;
1898 PyrInt32Array *pixels;
1899 PyrClass *pyrclass;
1900 NSRect rect;
1902 if(IsNil(pixelSlot))
1903 return errNone;
1905 slotIntVal(g->sp, &startIndex); // get the Array start index
1906 if(startIndex < 0)
1907 return errIndexOutOfRange;
1909 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1910 if(!image){
1911 SetNil(receiver);
1912 return errNone;
1915 err = slotGetNSRect(rectSlot, &rect);
1916 if (err) return err;
1917 rect = NSIntegralRect(rect);
1919 pyrclass = slotRawObject(pixelSlot)->classptr;
1920 if(pyrclass != class_int32array) {
1921 NSLog(@"prSCImage_loadPixels array argument is not a PyrInt32Array !");
1922 return errFailed;
1925 pixels = (PyrInt32Array*)slotRawObject(pixelSlot);
1926 bitmap = [image bitmapRepresentation];
1927 if(!bitmap) {
1928 NSLog(@"prSCImage_updatePixels failed retrieving valid NSBitmapImageRep...");
1929 return errIndexOutOfRange;
1932 width = [bitmap pixelsWide]; // RGBA
1933 height = [bitmap pixelsHigh];
1934 bytesPerRow = [bitmap bytesPerRow];
1935 bitmapData = [bitmap bitmapData];
1936 pixelsData = (uint32*)pixels->i + startIndex;
1938 // clipping
1939 rect.origin.x = sc_max(rect.origin.x, 0); // negative idx not allowed
1940 rect.origin.y = sc_max(rect.origin.y, 0); // negative idx not allowed
1941 rect.size.width = sc_min(rect.size.width, width); // clip to image width max
1942 rect.size.height = sc_min(rect.size.height, height); // clip to image height max
1944 // what is the best :
1945 // 1 - clipping + wrapping thus giving strange results ?
1946 // 2 - throw an error ?
1947 // just throw an error for now -> so no strange results and no scratching head... :)
1948 // zero tolerance >:^(
1950 ((rect.origin.x + rect.size.width) > width) ||
1951 ((rect.origin.y + rect.size.height) > height)
1953 return errIndexOutOfRange;
1955 bytesLeft = (bytesPerRow - (rect.size.width * 4));
1956 if( (pixels->size - startIndex) < (int)(rect.size.width * rect.size.height)) {
1957 post("Error: pixel array is not of good size ! should be %i bytes long from index: %i\n", (int)(rect.size.width * rect.size.height), startIndex);
1958 return errNone;
1961 [image lock];
1962 unsigned char alpha;
1963 bitmapData += ((int)rect.origin.y * bytesPerRow) + ((int)rect.origin.x * 4);
1964 for(y=0; y < rect.size.height; ++y) {
1965 for(x=0; x < rect.size.width; ++x) {
1966 alpha = PIXEL_ALPHA(*pixelsData);
1967 *bitmapData++ = COLOR_SCALE(PIXEL_RED(*pixelsData), alpha);
1968 *bitmapData++ = COLOR_SCALE(PIXEL_GREEN(*pixelsData), alpha);
1969 *bitmapData++ = COLOR_SCALE(PIXEL_BLUE(*pixelsData), alpha);
1970 *bitmapData++ = alpha;
1971 ++pixelsData;
1973 bitmapData += bytesLeft;
1975 [image unlock];
1977 [image setShouldSyncCIImage:YES];
1979 return errNone;
1982 int prSCImage_isAccelerated(struct VMGlobals *g, int numArgsPushed);
1983 int prSCImage_isAccelerated(struct VMGlobals *g, int numArgsPushed)
1985 if (!g->canCallOS) return errCantCallOS;
1987 PyrSlot *receiver = g->sp;
1989 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1990 if(!image){
1991 SetNil(receiver);
1992 return errNone;
1995 SetBool(receiver, [image isAccelerated]);
1997 return errNone;
2000 int prSCImage_setAccelerated(struct VMGlobals *g, int numArgsPushed);
2001 int prSCImage_setAccelerated(struct VMGlobals *g, int numArgsPushed)
2003 if (!g->canCallOS) return errCantCallOS;
2005 PyrSlot *receiver = g->sp - 1;
2006 PyrSlot *yornSlot = g->sp;
2008 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
2009 if(!image){
2010 SetNil(receiver);
2011 return errNone;
2014 [image setAccelerated:IsTrue(yornSlot)];
2016 return errNone;
2020 @class SCGraphView;
2021 // Currently it grabs a portion from the scgraphview
2022 // user should here pass the ABSOLUTE view bounds or a specific rect
2023 int prSCImage_imageFromSCWindowRect(struct VMGlobals *g, int numArgsPushed);
2024 int prSCImage_imageFromSCWindowRect(struct VMGlobals *g, int numArgsPushed)
2026 if (!g->canCallOS) return errCantCallOS;
2028 PyrSlot *receiver = g->sp - 2; // SCImage class
2029 PyrSlot *a = g->sp - 1; // SCWindow instance
2030 PyrSlot *b = g->sp; // SCRect instance - portion in the SCGraphView
2032 NSRect rect;
2033 NSBitmapImageRep *nsbitmap = nil;
2034 NSImage *nsimage = nil;
2035 SCGraphView *nsview = nil;
2036 SCImage *scimage = nil;
2037 int err = errNone;
2039 if(!IsPtr(&slotRawObject(a)->slots[0])) {
2040 return errFailed;
2043 nsview = (SCGraphView*)slotRawPtr(&slotRawObject(a)->slots[0]); // get SCGraphView instance
2044 err = slotGetNSRect(b, &rect);
2045 if (err)
2046 return err;
2048 [nsview lockFocus];
2049 nsbitmap = [[NSBitmapImageRep alloc]initWithFocusedViewRect:rect];
2050 [nsview unlockFocus];
2052 if(!nsbitmap)
2054 post("Error: Failed getting valid image from SCView !\n");
2056 else
2058 PyrObject* object = NULL;
2060 nsimage =
2061 [[NSImage alloc]initWithSize:NSMakeSize((float)[nsbitmap pixelsWide], (float)[nsbitmap pixelsHigh])];
2063 if(!nsimage) {
2064 post("Error: Failed creating valid NSImage !\n");
2065 SetNil(receiver);
2066 goto Bail;
2069 [nsimage addRepresentation:nsbitmap];
2071 scimage = [[SCImage alloc]initWithNSImage:nsimage];
2072 if(!scimage) {
2073 post("Error: Failed creating valid SCImage !\n");
2074 SetNil(receiver);
2075 return errNone;
2078 // Instantiate and set the SCObject
2079 object = newPyrSCImage(g);
2080 if(!object) {
2081 post("Error: Failed creating valid SCImage class !");
2082 [scimage release];
2083 SetNil(receiver);
2084 goto Bail;
2087 SetPtr(object->slots + 0, scimage);
2088 SetFloat(object->slots + 1, (float)[nsbitmap pixelsWide]);
2089 SetFloat(object->slots + 2, (float)[nsbitmap pixelsHigh]);
2090 SetObject(receiver, object);
2093 Bail:
2094 if(nsimage) [nsimage release];
2095 if(nsbitmap) [nsbitmap release];
2097 return errNone;
2100 #pragma mark --- SCImageFilter Primitive Set
2102 PyrClass* slotArrayGetCommonClass(PyrObject* array)
2104 int size = array->size;
2105 if(size == 0)
2106 return NULL;
2108 PyrClass *commonClass = slotRawObject(&array->slots[0])->classptr;
2110 for(int i=1; i < size; ++i) {
2111 if(slotRawObject(&array->slots[0])->classptr != commonClass)
2112 return NULL;
2115 return commonClass;
2118 BOOL nsClassIsKindOfClass(Class currentClass, Class match);
2119 BOOL nsClassIsKindOfClass(Class currentClass, Class match)
2121 if(currentClass == match)
2122 return YES;
2124 Class superClass;
2125 while( (superClass = class_getSuperclass(currentClass)) ) {
2126 if(superClass == match)
2127 return YES;
2130 return NO;
2133 id ciVectorFromSlot(PyrObject* slot);
2134 id ciVectorFromSlot(PyrObject* slot)
2136 ColorData values[4] = {0.f, 0.f, 0.f, 0.f};
2137 int size = slot->size, err;
2139 for (int i=0; i < size; ++i) {
2140 float val;
2141 err = slotFloatVal(slot->slots + i, &val);
2142 if(err) return nil;
2143 values[i] = val;
2146 return [CIVector vectorWithValues:values count:(size_t)4];
2149 int safeSlotFloatVal(PyrSlot* slot, float* value);
2150 int safeSlotFloatVal(PyrSlot* slot, float* value)
2152 if(IsNil(slot)) {
2153 *value = 0.f;
2154 return errNone;
2156 return slotFloatVal(slot, value);
2159 CGRect OutsetRect(CGRect r, const float dx, const float dy)
2161 CGRect ret = CGRectMake(r.origin.x - dx, r.origin.y - dy, r.size.width + dx + dx, r.size.height + dy + dy);
2162 return ret;
2165 CIImage* CropAndCenterCIImage(CIImage *image, CGRect originalExtent, CGRect* cropRegionRef);
2166 CIImage* CropAndCenterCIImage(CIImage *image, CGRect originalExtent, CGRect* cropRegionRef)
2168 #if SCIMAGE_FILTER_DEBUG
2169 NSLog(@"Cropping and centering CIImage %p", image);
2170 #endif
2171 CGRect extent = [image extent];
2172 CIFilter * ciFilter;
2174 // cropping
2175 if(cropRegionRef)
2177 CGRect cropRegion = *cropRegionRef;
2178 // Crop to specified rectangle.
2179 if(extent.size.width > cropRegion.size.width || extent.size.height > cropRegion.size.height)
2181 if(extent.origin.x > 0) {cropRegion.origin.x = extent.origin.x;}
2182 if(extent.origin.y > 0) {cropRegion.origin.y = extent.origin.y;}
2185 // quite strange but happens sometime...
2186 if(extent.origin.x < 0 && ((extent.origin.x + extent.size.width) < cropRegion.size.width)) {
2187 float diff = extent.size.width + extent.origin.x;
2188 cropRegion.origin.x -= (cropRegion.size.width - diff);
2191 if(extent.origin.y < 0 && ((extent.origin.y + extent.size.height) < cropRegion.size.height)) {
2192 float diff = extent.size.height + extent.origin.y;
2193 cropRegion.origin.y -= (cropRegion.size.height - diff);
2196 #if SCIMAGE_FILTER_DEBUG
2197 NSLog(@"SCImageFilter: Cropping To Size: (%4.2f, %4.2f, %4.2f, %4.2f) fromExtent: (%4.2f, %4.2f, %4.2f, %4.2f)",
2198 cropRegion.origin.x,cropRegion.origin.y,cropRegion.size.width,cropRegion.size.height,
2199 extent.origin.x,extent.origin.y,extent.size.width,extent.size.height
2201 #endif
2203 ciFilter = [CIFilter filterWithName:@"CICrop"]; // crop the final result according
2204 [ciFilter setValue:image forKey:@"inputImage"];
2205 [ciFilter setValue:[CIVector vectorWithX:cropRegion.origin.x Y:cropRegion.origin.y Z:cropRegion.size.width W:cropRegion.size.height] forKey:@"inputRectangle"];
2206 image = [ciFilter valueForKey:@"outputImage"];
2207 extent = [image extent];
2208 #if SCIMAGE_FILTER_DEBUG
2209 NSLog(@"SCImageFilter: After CROP Extent: (%4.2f, %4.2f, %4.2f, %4.2f)", extent.origin.x,extent.origin.y,extent.size.width, extent.size.height);
2210 #endif
2214 // translating origin and center it if we need it
2215 // currently auto translation is now deactivated by default - let the user choose
2218 if(extent.origin.x != 0 || extent.origin.y != 0) {
2219 ciFilter = [CIFilter filterWithName:@"CIAffineTransform"];
2220 NSAffineTransform* trs = [NSAffineTransform transform];
2221 [trs translateXBy:-extent.origin.x yBy:-extent.origin.y]; // move to zero
2222 [ciFilter setValue:trs forKey:@"inputTransform"];
2223 [ciFilter setValue:image forKey:@"inputImage"];
2224 image = [ciFilter valueForKey:@"outputImage"];
2225 #if SCIMAGE_FILTER_DEBUG
2226 NSLog(@"SCImageFilter: After Translate: (%4.2f, %4.2f, %4.2f, %4.2f)", extent.origin.x,extent.origin.y,extent.size.width, extent.size.height);
2227 #endif
2231 return image;
2234 id nsciObjectFromSlot(PyrSlot *slot, Class attributeClass, NSString *attributeType);
2235 id nsciObjectFromSlot(PyrSlot *slot, Class attributeClass, NSString *attributeType)
2237 int err;
2238 if(attributeClass == [CIImage class] || attributeType == kCIAttributeTypeGradient)
2240 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(slot)->slots);
2241 return [image ciimage];
2243 else if(isKindOfSlot(slot, s_scimage->u.classobj) && (attributeClass == NULL) && (attributeType == NULL)) // here returns the CISampler by default
2245 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(slot)->slots);
2246 return [CISampler samplerWithImage:[image ciimage]];
2248 // else if(isKindOfSlot(slot, s_vector->u.classobj)) {
2250 else if(isKindOfSlot(slot, s_array->u.classobj)) {
2251 #if SCIMAGE_FILTER_DEBUG
2252 NSLog(@"Creating CIVector From Vector Slot");
2253 #endif
2255 return ciVectorFromSlot(slotRawObject(slot));
2258 else if(isKindOfSlot(slot, s_color->u.classobj))
2260 PyrSlot* color_slots = slotRawObject(slot)->slots;
2261 float cr,cg,cb,ca;
2262 err = slotFloatVal(color_slots+0, &cr);
2263 if (err) cr = 0.f;
2264 err = slotFloatVal(color_slots+1, &cg);
2265 if (err) cg = 0.f;
2266 err = slotFloatVal(color_slots+2, &cb);
2267 if (err) cb = 0.f;
2268 err = slotFloatVal(color_slots+3, &ca);
2269 if (err) ca = 1.f;
2271 #if SCIMAGE_FILTER_DEBUG
2272 NSLog(@"Creating CIColor/NSColor From Color Slot");
2273 #endif
2275 if(attributeClass == [NSColor class])
2277 return [NSColor colorWithCalibratedRed:cr green:cg blue:cb alpha:ca];
2279 return [CIColor colorWithRed:cr green:cg blue:cb alpha:ca];
2281 else if(isKindOfSlot(slot, s_array->u.classobj))
2283 if(attributeClass == [NSAffineTransform class]) {
2284 #if SCIMAGE_FILTER_DEBUG
2285 NSLog(@"Creating NSAffineTransform From Array Slot");
2286 #endif
2287 // array containing [Tx, Ty, Sx, Sy, Rot in radians]
2288 PyrObject* array = slotRawObject(slot);
2289 int size = sc_min(array->size, 5), err;
2290 float attributes[5] = {0.f, 0.f, 1.f, 1.f, 0.f};
2291 for(int i=0; i < size; ++i) {
2292 err = slotFloatVal(array->slots+i, attributes+i);
2293 if(err)
2294 attributes[i] = 0.f;
2297 NSAffineTransform *transform = [NSAffineTransform transform];
2298 [transform translateXBy:attributes[0] yBy:attributes[1]];
2299 [transform scaleXBy:attributes[2] yBy:attributes[3]];
2300 [transform rotateByRadians:attributes[4]];
2301 return transform;
2304 if(attributeClass == [CIVector class] || attributeClass == NULL) {
2305 #if SCIMAGE_FILTER_DEBUG
2306 NSLog(@"Creating CIVector From Array Slot");
2307 #endif
2308 return ciVectorFromSlot(slotRawObject(slot));
2311 #if 0
2312 if(nsClassIsKindOfClass(attributeClass, [NSData class])) {
2313 // handle specific case here...
2314 // mostly for CIColorCube Filter
2315 #if SCIMAGE_FILTER_DEBUG
2316 NSLog(@"Creating NSData From Array Slot");
2317 #endif
2318 PyrClass *support = slotArrayGetCommonClass(slotRawObject(slot));
2320 support == NULL ||
2323 support != s_int8array->u.classobj &&
2324 support != s_int16array->u.classobj &&
2325 support != s_int32array->u.classobj &&
2326 support != s_doublearray->u.classobj &&
2327 support != class_floatarray->u.classobj
2331 return nil;
2334 #endif
2336 else if(IsTrue(slot) || IsFalse(slot) )
2338 #if SCIMAGE_FILTER_DEBUG
2339 NSLog(@"Creating NSNumber From Boolean Slot");
2340 #endif
2341 return [NSNumber numberWithBool:(IsTrue(slot))];
2343 else if(IsFloat(slot) || IsInt(slot))
2345 #if SCIMAGE_FILTER_DEBUG
2346 NSLog(@"Creating NSNumber From Float/Int Slot");
2347 #endif
2349 float val;
2350 err = safeSlotFloatVal(slot, &val);
2351 if(err) return nil;
2353 if(attributeType == kCIAttributeTypeBoolean) {
2354 return [NSNumber numberWithBool:val > 0];
2356 return [NSNumber numberWithFloat:val];
2359 post("Non supported type of class: %s", slotRawObject(slot)->classptr->name);
2360 return nil; // failure
2364 int prSCImageFilter_ApplyMultiple(struct VMGlobals *g, int numArgsPushed);
2365 int prSCImageFilter_ApplyMultiple(struct VMGlobals *g, int numArgsPushed)
2367 PyrSlot *receiver = g->sp - 4;
2368 PyrSlot *filtersSlot = g->sp - 3;
2369 PyrSlot *inPlace = g->sp - 2;
2370 PyrSlot *updateRegionSlot = g->sp - 1;
2371 PyrSlot *cropRegionSlot = g->sp;
2372 int err = errNone;
2374 SCImage *scimage = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
2375 if(!scimage){
2376 SetNil(receiver);
2377 return errNone;
2380 int numberOfFilters;
2381 PyrObject* scFilter;
2382 PyrObject *arguments;
2383 PyrSymbol *key;
2384 PyrSlot *value;
2385 id nsvalue;
2386 //NSArray *inputKeys;
2387 NSString *nskey;
2388 Class valueClass;
2389 CIImage* ciimage;
2390 CIFilter* ciFilter;
2391 CGRect origExtent = [scimage extent];
2392 CGRect destExtent;
2393 //CGPoint delta;
2395 numberOfFilters = slotRawObject(filtersSlot)->size;
2397 // Get the CIImage rep in the SCImage
2398 if(![scimage isAccelerated])
2399 [scimage syncCIImage]; // update ciimage rep
2400 ciimage = [scimage ciimage]; // now we can get it
2402 // Iterate through all the filters
2403 for(int i=0; i < numberOfFilters; ++i)
2405 int size = 0;
2407 scFilter = slotRawObject(&(slotRawObject(filtersSlot)->slots[i]));
2409 if(IsFalse(scFilter->slots+3)) // filter enabled ?
2410 continue;
2412 ciFilter = [CIFilter filterWithName: nsStringFromSlot(scFilter->slots+0)]; // filter name
2413 if(!ciFilter) return errFailed;
2414 #if SCIMAGE_FILTER_DEBUG
2415 NSLog(@"SCImageFilter --- Setting Filter Named %@", nsStringFromSlot(scFilter->slots+0));
2416 #endif
2417 [ciFilter setDefaults];
2419 arguments = slotRawObject(&scFilter->slots[2]); // filter values
2420 size = (arguments->size >> 1);
2422 for(int i=0; i < size; ++i)
2424 key = slotRawSymbol(arguments->slots + (i<<1));
2425 value = arguments->slots + ((i<<1) + 1);
2426 nskey = [NSString stringWithFormat:@"input%c%s", toupper(*key->name), key->name+1];
2427 valueClass = NSClassFromString( [[[ciFilter attributes] objectForKey:nskey] objectForKey:@"CIAttributeClass"]);
2428 nsvalue = nsciObjectFromSlot(value, valueClass, [[ciFilter attributes] objectForKey:kCIAttributeType]);
2430 if(nsvalue)
2432 #if SCIMAGE_FILTER_DEBUG
2433 NSLog(@"Setting Key: %@ Value: %@ Class: %@", nskey, nsvalue, valueClass);
2434 #endif
2435 [ciFilter setValue:nsvalue forKey:nskey];
2437 else
2439 post("Error: bad (nil) slot value for Filter: %s key %s, NSObject conversion failed for class %s !", slotRawSymbol(&scFilter->slots[0])->name, [nskey UTF8String], class_getName(valueClass));
2443 // concatenate filters
2444 if([[ciFilter inputKeys]containsObject:@"inputImage"])
2446 [ciFilter setValue:ciimage forKey:@"inputImage"];
2449 // get results
2450 ciimage = [ciFilter valueForKey:@"outputImage"];
2451 ////
2454 destExtent = [ciimage extent];
2456 #if SCIMAGE_MAC_OS_10_5
2458 //CGRectIsInfinite(destExtent) || // infinite rects are mosly results from generator filters
2459 !IsNil(updateRegionSlot) // better to keep original extent if constrained update
2462 destExtent = origExtent;
2464 else
2466 #endif
2467 // if the rect is not infinite :
2468 // 1 - crop the image if a cropRegion is specified
2469 // 2 - translate the image to align origin of extent with coordindate origin of image (0,0)
2470 CGRect cropRegion;
2471 if(IsNil(cropRegionSlot)) {
2472 #if SCIMAGE_FILTER_DEBUG
2473 NSLog(@"SCImageFilter: Nil Crop Region - creating from %i, %i maximum crop size", SCCIIMAGE_MAX_WIDTH, SCCIIMAGE_MAX_HEIGHT);
2474 #endif
2475 cropRegion = OutsetRect(origExtent, floorf((SCCIIMAGE_MAX_WIDTH - origExtent.size.width) * 0.5), floorf((SCCIIMAGE_MAX_HEIGHT - origExtent.size.height) * 0.5));
2476 } else {
2477 err = slotGetNSRect(cropRegionSlot, (NSRect*)&cropRegion);
2478 if(err) return err;
2480 ciimage = CropAndCenterCIImage(ciimage, origExtent, &cropRegion);
2481 destExtent = [ciimage extent];
2483 #if SCIMAGE_MAC_OS_10_5
2485 #endif
2488 PyrObject *receiverObj;
2489 if(IsTrue(inPlace))
2491 // Here the caller should be directly updated !
2492 // Size will be equal if :
2493 // 1 - the filter does not extend the image bounds (modified / infinite extent)
2494 // 2 - the update region slot if not nil to constrain updates
2495 if(CGSizeEqualToSize(destExtent.size, origExtent.size))
2497 #if SCIMAGE_MAC_OS_10_5
2498 if(!IsNil(updateRegionSlot))
2500 CGRect updateRegion;
2501 err = slotGetNSRect(updateRegionSlot, (NSRect*)&updateRegion);
2502 if(err) return err;
2503 // Invert SC Coordinates
2504 updateRegion.origin.y = [[scimage accumulator]extent].size.height - (updateRegion.origin.y + updateRegion.size.height);
2505 [[scimage accumulator] setImage:ciimage dirtyRect:updateRegion];
2507 else
2509 [[scimage accumulator] setImage:ciimage];
2511 #else
2512 [ciimage retain];
2513 [scimage->_ciimage release];
2514 scimage->_ciimage = ciimage;
2515 #endif
2516 [scimage setShouldSyncBitmap:YES]; // keep the sync info
2517 return errNone;
2519 else
2521 receiverObj = slotRawObject(receiver);
2523 #if SCIMAGE_FILTER_DEBUG
2524 NSLog(@"SCImageFilter: New Extent (%4.2f, %4.2f, %4.2f, %4.2f)", destExtent.origin.x, destExtent.origin.y, destExtent.size.width, destExtent.size.height);
2525 #endif
2527 else
2529 receiverObj = newPyrSCImage(g);
2530 if(!receiverObj)
2532 post("Failed creating valid SCImage class !");
2533 return errFailed;
2537 #if SCIMAGE_DEBUG || SCIMAGE_FILTER_DEBUG
2538 NSLog(@"SCImageFilter: Creating New SCImage Class");
2539 NSLog(@"SCImageFilter: OriginalImage Extent: (%4.2f, %4.2f, %4.2f, %4.2f) NewImage Extent: (%4.2f, %4.2f, %4.2f, %4.2f)",
2540 origExtent.origin.x, origExtent.origin.y, origExtent.size.width, origExtent.size.height,
2541 destExtent.origin.x,destExtent.origin.y,destExtent.size.width, destExtent.size.height
2543 #endif
2545 SCImage *newSCImage = [[SCImage alloc]initWithCIImage:ciimage extent:destExtent format:[scimage format]];
2546 if(!newSCImage)
2548 post("Error: Failed creating valid CIImage cache !!!");
2549 return errFailed;
2552 value = receiverObj->slots;
2553 SetPtr(value + 0, newSCImage);
2554 SetFloat(value + 1, destExtent.size.width);
2555 SetFloat(value + 2, destExtent.size.height);
2557 if(receiverObj == slotRawObject(receiver))
2558 [scimage release];
2559 else
2560 SetObject(receiver, receiverObj);
2561 return errNone;
2565 int prSCImageFilter_Apply(struct VMGlobals *g, int numArgsPushed);
2566 int prSCImageFilter_Apply(struct VMGlobals *g, int numArgsPushed)
2568 PyrSlot *receiver = g->sp - 3;
2569 PyrSlot *filterNameSlot = g->sp - 2;
2570 PyrSlot *argumentsSlot = g->sp - 1;
2571 PyrSlot *inPlace = g->sp;
2573 SCImage *scimage = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
2574 if(!scimage){
2575 SetNil(receiver);
2576 return errNone;
2579 CIFilter* ciFilter = [CIFilter filterWithName: nsStringFromSlot(filterNameSlot)];
2580 if(!ciFilter)
2581 return errFailed;
2583 [ciFilter setDefaults];
2585 PyrObject *arguments = slotRawObject(argumentsSlot); // array
2586 PyrSymbol *key;
2587 PyrSlot *value;
2588 NSString *nskey;
2589 Class valueClass;
2590 id nsvalue;
2592 int size = (arguments->size >> 1);
2594 for(int i=0; i < size; ++i) {
2595 key = (PyrSymbol*)slotRawSymbol(arguments->slots + (i<<1));
2596 value = arguments->slots + ((i<<1) + 1);
2597 nskey = [NSString stringWithFormat:@"input%c%s", toupper(*key->name), key->name+1];
2598 valueClass = NSClassFromString( [[[ciFilter attributes] objectForKey:nskey] objectForKey:@"CIAttributeClass"]);
2599 nsvalue = nsciObjectFromSlot(value, valueClass, [[ciFilter attributes] objectForKey:kCIAttributeType]);
2600 if(nsvalue) {
2601 #if SCIMAGE_FILTER_DEBUG
2602 NSLog(@"Setting Key: %@ Value: %@ Class: %@", nskey, nsvalue, valueClass);
2603 #endif
2604 [ciFilter setValue:nsvalue forKey:nskey];
2605 }else{
2606 post("Error: bad (nil) slot value for key %s, NSObject conversion failed for class %s !", [nskey UTF8String], class_getName(valueClass));
2610 if(IsTrue(inPlace)) {
2611 [scimage applyFilter:ciFilter]; // in place filter
2613 else {
2614 #if SCIMAGE_DEBUG
2615 NSLog(@"Creating New SCImage Class");
2616 #endif
2618 scimage = [scimage imageFilteredWith:ciFilter]; // filtered copy
2619 PyrObject* object = newPyrSCImage(g);
2620 PyrSlot *slots = object->slots;
2621 SetPtr(slots + 0, scimage);
2622 SetFloat(slots + 1, (float)[scimage width]);
2623 SetFloat(slots + 2, (float)[scimage height]);
2624 SetObject(receiver, object);
2627 return errNone;
2632 * Currently only cache the CIKernel object + install a finalizer on it
2634 int cachePyrKernel(VMGlobals *g, PyrObject* kernelObject)
2636 NSString* kernelCmd = nsStringFromSlot(kernelObject->slots+0);
2637 if(!kernelCmd) {
2638 post("Error: Kernel string command does not seem valid !\n");
2639 return errFailed;
2642 id kernel = [CIKernel kernelsWithString:kernelCmd];
2643 if(!kernel) {
2644 post("Error: Failed creating valid Kernel from shader string !!\n");
2645 return errFailed;
2648 // Create the Kernel
2649 kernel = [(NSArray*)kernel objectAtIndex:0];
2650 if(!kernel) {
2651 post("Error: Failed retrieving valid CIKernel !");
2652 return errFailed;
2654 [kernel retain];
2656 if( !IsNil(kernelObject->slots+4) ) {
2657 id current = (id)slotRawPtr(&kernelObject->slots[4]);
2658 if(current)
2659 [current release];
2662 SetPtr(kernelObject->slots+4, kernel);
2664 if( IsNil(kernelObject->slots+5) ) {
2665 #if SCIMAGE_DEBUG
2666 post("install finalizer scimagekernel (%p)\n", kernelObject);
2667 #endif
2668 InstallFinalizer(g, kernelObject, 5, FinalizeSCImageKernelObject);
2670 #if SCIMAGE_DEBUG
2671 post("caching scimagekernel (%p)\n", kernelObject);
2672 #endif
2673 return errNone;
2676 int prSCImageKernel_Compile(struct VMGlobals *g, int numArgsPushed);
2677 int prSCImageKernel_Compile(struct VMGlobals *g, int numArgsPushed)
2679 PyrSlot* receiver = g->sp;
2680 return cachePyrKernel(g, slotRawObject(receiver));
2684 * This function is intended to apply a specific CIKernel to the image
2685 * Still experimental
2687 int prSCImageFilter_ApplyKernel(struct VMGlobals *g, int numArgsPushed);
2688 int prSCImageFilter_ApplyKernel(struct VMGlobals *g, int numArgsPushed)
2690 PyrSlot *receiver = g->sp - 3;
2691 PyrSlot *kernelSlot = g->sp - 2;
2692 PyrSlot *cropRegionSlot = g->sp - 1;
2693 PyrSlot *inPlace = g->sp;
2695 PyrSlot *argumentSlot;
2696 SCImage *newSCImage=nil;
2697 SCImage *scimage;
2698 PyrObject *kernelObject=nil;
2699 CIKernel *kernel=nil;
2701 int err;
2702 CGRect destExtent;
2703 CGRect cropRegion;
2704 CGRect maxRect;
2707 err = errNone;
2708 scimage = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
2709 if(!scimage){
2710 SetNil(receiver);
2711 return errNone;
2714 [scimage setAccelerated:YES];
2715 CIFilter* ciFilter = [[CIFilter new]autorelease];
2717 ///// Setting and caching the Kernel Object
2718 kernelObject = slotRawObject(kernelSlot);
2719 if(IsNil(kernelObject->slots+4)) {
2720 err = cachePyrKernel(g, kernelObject);
2721 if(err) return err;
2723 argumentSlot = kernelObject->slots+1;
2724 kernel = (CIKernel*)(slotRawPtr(&kernelObject->slots[4]));
2725 if(!kernel)
2726 return errFailed;
2728 // Get the extent of the image
2729 CGRect extent = [[scimage ciimage]extent];
2731 #if SCIMAGE_FILTER_DEBUG
2732 NSLog(@"SCImageFilter: CIKernel created Successfully !");
2733 #endif
2734 // Setting the Argument Array + PyrObject->Cocoa conversion
2735 // from the docs:
2737 You can pass the following types to a kernel routine:
2738 sampler:
2739 Requires a CISampler object when applied. // directly added in SCApp
2740 __table:
2741 A qualifier for a sampler type.
2742 float, vec2, vec3, vec4:
2743 Requires an NSNumber or CIVector.
2744 __color:
2745 A color that will be matched to the CIContext working color space when passed into the program.
2746 It requires a CIColor object when applied. To the kernel program it appears to be a vec4 type in premultiplied RGBA format.
2749 NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:16];
2750 if(!arguments) {
2751 post("Error: memory error ? allocation failed for NSMutableArray. applying Kernel aborted !");
2752 return errFailed;
2755 #if SCIMAGE_FILTER_DEBUG
2756 NSLog(@"SCImageFilter: CIKernel setting Arguments !");
2757 #endif
2758 // set all the arguments
2759 if( !IsNil(argumentSlot) ) { // iterate through all the arguments
2760 PyrObject * pyrArray = slotRawObject(argumentSlot);
2761 int size = pyrArray->size;
2762 for(int i=0; i < size; ++i) {
2763 id object = nsciObjectFromSlot(pyrArray->slots+i, NULL, NULL);
2764 if(object) {
2765 [arguments addObject:object];
2767 else
2768 return errFailed; // Kernels require no bad arguments
2772 // Create the Option Dictionary that contains additional info about final rendering
2773 // kCIApplyOptionDefinition : DOD of image
2774 // kCIApplyOptionExtent : size of the resulting image
2775 NSMutableDictionary * dictionary = [[[NSDictionary dictionary] mutableCopy] autorelease];
2777 [dictionary
2778 setValue:[[scimage ciimage] definition] // better to pass directly the filterShape
2779 forKey:kCIApplyOptionDefinition
2782 // Set the Extent of the result image if one is specified
2784 if(!IsNil(maxBounds)) {
2785 CGRect maxRect;
2786 err = slotGetNSRect(maxBounds, (NSRect*)&maxRect);
2787 if(err) goto Bail;
2788 [dictionary setValue:
2789 [NSArray arrayWithObjects:
2790 [NSNumber numberWithFloat:maxRect.origin.x], // x
2791 [NSNumber numberWithFloat:maxRect.origin.y], // y
2792 [NSNumber numberWithFloat:maxRect.size.width], // w
2793 [NSNumber numberWithFloat:maxRect.size.height] // h
2794 ,nil
2796 forKey:kCIApplyOptionExtent
2801 #if SCIMAGE_FILTER_DEBUG
2802 NSLog(@"SCImageFilter: CIKernel created Options successfully !");
2803 #endif
2805 CIImage *ciimage;
2806 @try {
2807 ciimage = [ciFilter apply:kernel arguments:arguments options:dictionary];
2809 // if the rect is not infinite :
2810 // 1 - crop the image if a cropRegion is specified
2811 // 2 - translate the image to align origin of extent with coordindate origin of image (0,0)
2813 if(IsNil(cropRegionSlot)) {
2814 cropRegion = OutsetRect(extent, floorf((SCCIIMAGE_MAX_WIDTH - extent.size.width) * 0.5), floorf((SCCIIMAGE_MAX_HEIGHT - extent.size.height) * 0.5));
2815 } else {
2816 err = slotGetNSRect(cropRegionSlot, (NSRect*)&cropRegion);
2817 if(err) return err;
2820 ciimage = CropAndCenterCIImage(ciimage, extent, &cropRegion);
2822 destExtent = [ciimage extent];
2824 #if SCIMAGE_FILTER_DEBUG
2825 NSLog(@"SCImageFilter: applied Kernel Successfully !");
2826 #endif
2828 // Setting the PyrSlot
2829 PyrObject* receiverObj;
2830 if(IsTrue(inPlace))
2832 if( CGSizeEqualToSize(extent.size, destExtent.size) ) {
2833 #if SCIMAGE_MAC_OS_10_5
2834 [[scimage accumulator] setImage:ciimage];
2835 #else
2836 [scimage->_ciimage release];
2837 scimage->_ciimage = [ciimage retain];
2838 #endif
2839 [scimage setShouldSyncBitmap:YES]; // invalidates bitmap
2840 return errNone;
2841 }else{
2842 NSLog(@"CIKernel different size returned");
2843 receiverObj = slotRawObject(receiver);
2846 else
2848 receiverObj = newPyrSCImage(g);
2851 #if SCIMAGE_FILTER_DEBUG
2852 NSLog(@"SCImageFilter: Creating New SCImage Class");
2853 #endif
2855 newSCImage = [[SCImage alloc]initWithCIImage:ciimage extent:destExtent format:[scimage format]];
2857 PyrSlot *slots = receiverObj->slots;
2858 [(id)slotRawPtr(slots) release];
2860 SetPtr(slots + 0, newSCImage);
2861 SetFloat(slots + 1, destExtent.size.width);
2862 SetFloat(slots + 2, destExtent.size.height);
2864 if(receiverObj != slotRawObject(receiver)) {
2865 SetObject(receiver, receiverObj);
2868 @catch ( NSException *e )
2870 //if([[e name]isEqualToString: @"CIKernelMissingArgument"])
2871 post("Error: %s\n", [[NSString stringWithFormat:@"%@ %@", @"SCImageKernel", e]UTF8String]);
2872 return errFailed;
2875 return errNone;
2878 int prSCImageFilter_GetAttributeMinMax(struct VMGlobals *g, int numArgsPushed)
2880 PyrSlot *receiver = g->sp - 2;
2881 PyrSlot *filterAttributeSlot= g->sp - 1;
2882 PyrSlot *returnedArraySlot = g->sp;
2883 CIFilter *ciFilter;
2885 ciFilter = [CIFilter filterWithName: nsStringFromSlot(slotRawObject(receiver)->slots+0)]; // filter name
2886 if(!ciFilter) return errFailed;
2888 NSDictionary* attrDict = [ciFilter attributes];
2889 NSString *key = nsStringFromSlot(filterAttributeSlot);
2890 id attribute;
2892 #if SCIMAGE_FILTER_DEBUG
2893 NSLog(@"Checking for CIFilter MinMax of attribute: %@", key);
2894 #endif
2896 if( !key ) {
2897 SetNil(receiver);
2898 return errNone;
2900 // goto BailErr;
2902 const char* cStr = [key cStringUsingEncoding:NSASCIIStringEncoding];
2903 attribute = [attrDict objectForKey: [NSString stringWithFormat:@"input%c%s", toupper(*cStr), cStr+1]];
2904 if( !attribute ) {
2905 post("SCImageFilter : no attributes named %s found !\n", [key UTF8String]);
2906 SetNil(receiver);
2907 return errNone;
2908 // goto BailErr;
2911 PyrObject* pyrArray = slotRawObject(returnedArraySlot); // should be an array of size 3
2912 NSString* attrType = [attribute objectForKey:kCIAttributeType];
2914 id minmax[3];
2915 minmax[0] = [attribute objectForKey: kCIAttributeSliderMin] ? [attribute objectForKey: kCIAttributeSliderMin] : [attribute objectForKey: kCIAttributeMin];
2916 minmax[1] = [attribute objectForKey: kCIAttributeSliderMax] ? [attribute objectForKey: kCIAttributeSliderMax] : [attribute objectForKey: kCIAttributeMax];
2917 minmax[2] = [attribute objectForKey: kCIAttributeDefault];
2919 if(!minmax[0] && !minmax[1] && !minmax[2]){ // if no slider min or slider max or default - set to nil directly
2920 //goto BailErr;
2921 SetNil(receiver);
2922 return errNone;
2925 if(attrType == kCIAttributeTypeBoolean)
2927 SetFalse(pyrArray->slots+0);
2928 SetTrue(pyrArray->slots+1);
2929 SetBool(pyrArray->slots+2, ([ minmax[2] floatValue ] > 0.f));
2931 else if(
2932 attrType == kCIAttributeTypeAngle || attrType == kCIAttributeTypeDistance ||
2933 attrType == kCIAttributeTypeScalar || attrType == kCIAttributeTypeTime
2936 if(minmax[0])
2937 SetFloat(pyrArray->slots+0, [minmax[0] doubleValue]);
2938 else
2939 SetNil(pyrArray->slots+0);
2941 if(minmax[1])
2942 SetFloat(pyrArray->slots+1, [minmax[1] doubleValue]);
2943 else
2944 SetNil(pyrArray->slots+1);
2946 if(minmax[2])
2947 SetFloat(pyrArray->slots+2, [minmax[2] doubleValue]);
2948 else
2949 SetNil(pyrArray->slots+1);
2951 else if( attrType == kCIAttributeTypePosition || attrType == kCIAttributeTypeOffset )
2953 // 2-element vector
2954 PyrObject* vector[3];
2955 for(int i=0; i < 3; ++i) {
2956 if( ! minmax[i] ) {
2957 SetNil(pyrArray->slots+i); continue;
2959 vector[i] = newPyrArray(g->gc, sizeof(double) * 2, 0, true);
2960 SetFloat( vector[i]->slots+0, [ (CIVector*)minmax[i] X ]);
2961 SetFloat( vector[i]->slots+1, [ (CIVector*)minmax[i] Y ]);
2962 vector[i]->size = 2;
2963 SetObject(pyrArray->slots+i, vector[i]);
2964 g->gc->GCWrite(pyrArray, vector[i]);
2967 else if( attrType == kCIAttributeTypePosition3 )
2969 // 3-element vector
2970 PyrObject* vector[3];
2971 for(int i=0; i < 3; ++i) {
2972 if( ! minmax[i] ) {
2973 SetNil(pyrArray->slots+i); continue;
2975 vector[i] = newPyrArray(g->gc, sizeof(double) * 3, 0, true);
2976 SetFloat( vector[i]->slots+0, [ (CIVector*)minmax[i] X ]);
2977 SetFloat( vector[i]->slots+1, [ (CIVector*)minmax[i] Y ]);
2978 SetFloat( vector[i]->slots+2, [ (CIVector*)minmax[i] Z ]);
2979 vector[i]->size = 3;
2980 SetObject(pyrArray->slots+i, vector[i]);
2981 g->gc->GCWrite(pyrArray, vector[i]);
2984 else if( attrType == kCIAttributeTypeRectangle )
2986 // 4-element vector
2987 PyrObject* vector[3];
2988 for(int i=0; i < 3; ++i) {
2989 if( ! minmax[i] ) {
2990 SetNil(pyrArray->slots+i); continue;
2992 vector[i] = newPyrArray(g->gc, sizeof(double) * 4, 0, true);
2993 SetFloat( vector[i]->slots+0, [ (CIVector*)minmax[i] X ]);
2994 SetFloat( vector[i]->slots+1, [ (CIVector*)minmax[i] Y ]);
2995 SetFloat( vector[i]->slots+2, [ (CIVector*)minmax[i] Z ]);
2996 SetFloat( vector[i]->slots+3, [ (CIVector*)minmax[i] W ]);
2997 vector[i]->size = 4;
2998 SetObject(pyrArray->slots+i, vector[i]);
2999 g->gc->GCWrite(pyrArray, vector[i]);
3002 else if( attrType == kCIAttributeTypeOpaqueColor )
3004 // 4-element vector
3005 PyrObject* vector[3];
3006 for(int i=0; i < 3; ++i) {
3007 vector[i] = ::instantiateObject(g->gc, s_color->u.classobj, 0, false, true);
3008 SetFloat( vector[i]->slots+0, (double)[ (CIColor*)minmax[i] red ]);
3009 SetFloat( vector[i]->slots+1, (double)[ (CIColor*)minmax[i] green ]);
3010 SetFloat( vector[i]->slots+2, (double)[ (CIColor*)minmax[i] blue ]);
3011 SetFloat( vector[i]->slots+3, (double)[ (CIColor*)minmax[i] alpha ]);
3012 SetObject(pyrArray->slots+i, vector[i]);
3013 g->gc->GCWrite(pyrArray, vector[i]);
3016 else {
3017 SetNil(receiver);
3018 return errNone;
3020 // goto BailErr;
3022 SetObject(receiver, pyrArray);
3023 return errNone;
3025 // BailErr:
3026 // SetNil(receiver);
3027 // return errNone;
3030 int prSCImageFilter_Attributes(struct VMGlobals *g, int numArgsPushed);
3031 int prSCImageFilter_Attributes(struct VMGlobals *g, int numArgsPushed)
3033 PyrSlot *receiver = g->sp - 1;
3034 PyrSlot *filterNameSlot = g->sp;
3035 CIFilter *filter;
3037 filter = [CIFilter filterWithName: nsStringFromSlot(filterNameSlot)];
3038 if(!filter)
3039 return errFailed;
3041 NSDictionary* attr = [filter attributes];
3042 NSArray *inputKeys = [filter inputKeys];
3044 NSEnumerator* e = [inputKeys objectEnumerator];
3045 NSString *key;
3047 int maxsize, i=0;
3048 maxsize = [inputKeys count] * 2;
3049 if([inputKeys containsObject:@"inputImage"]) {
3050 maxsize -= 2; // remove the inputImage
3052 PyrSymbolArray* returnObject = newPyrSymbolArray(g->gc, maxsize, 0, true);
3053 returnObject->size = maxsize;
3055 const char* cStr;
3056 while( (key = [e nextObject]) )
3058 if(i >= maxsize) return errNone; // in case
3059 if(![key isEqualToString:@"inputImage"]) { // we do not need it
3060 cStr = [[key substringFromIndex:5] cStringUsingEncoding:NSASCIIStringEncoding]; // remove the 'input'
3061 returnObject->symbols[i++] = getsym([ [NSString stringWithFormat: @"%c%s", tolower(*cStr), (cStr + 1)] UTF8String]);
3062 returnObject->symbols[i++] = getsym([ [ [ attr objectForKey: key] objectForKey: @"CIAttributeClass" ] UTF8String ]);
3066 SetObject(receiver, returnObject);
3068 return errNone;
3071 int prSCImageFilter_NamesInCategory(struct VMGlobals *g, int numArgsPushed);
3072 int prSCImageFilter_NamesInCategory(struct VMGlobals *g, int numArgsPushed)
3074 PyrSlot *receiver, *category, *returnArray;
3075 NSString *type;
3076 NSArray *array;
3078 returnArray = g->sp;
3079 category = g->sp- 1;
3080 receiver = g->sp - 2;
3082 //PyrObject *returnObject = slotRawObject(returnArray);
3083 //int maxsize = MAXINDEXSIZE(returnObject);
3085 if (!IsSym(category)) return errWrongType;
3087 type = nsStringFromSlot(category);
3088 if(!type) return errFailed;
3090 array = [CIFilter filterNamesInCategory:type];
3091 if(!array) return errFailed;
3093 int maxsize = [array count];
3094 PyrSymbolArray* returnObject = newPyrSymbolArray(g->gc, maxsize, 0, true);
3095 returnObject->size = maxsize;
3097 int i=0;
3098 NSEnumerator *e = [array objectEnumerator];
3101 while( (type = [e nextObject]) )
3103 if(i >= maxsize) break; // in case
3104 returnObject->symbols[i] = getsym([type UTF8String]);
3105 ++i;
3108 SetObject(receiver, returnObject);
3110 return errNone;
3113 #pragma mark - InitSCImagePrims
3115 void initSCImagePrimitives()
3117 int base, index;
3119 s_scimage = getsym("SCImage");
3120 s_scfilter = getsym("SCImageFilter");
3121 s_scimagekernel = getsym("SCImageKernel");
3123 #if SCIMAGE_DEBUG
3124 NSLog(@"---- SCIMAGE: Init Primitives ----");
3125 NSLog(@"---- SCIMAGE: Using Mac OS 10.%c Version ----", (SCIMAGE_MAC_OS_10_5 == 1) ? '5' : '4');
3126 #endif
3128 if(!gCIFilterPlugInsLoaded) {
3129 #if SCIMAGE_DEBUG
3130 NSLog(@"---- SCIMAGE: Loading CIFilter PlugIns ----");
3131 #endif
3132 [CIPlugIn loadAllPlugIns];
3133 gCIFilterPlugInsLoaded = YES;
3136 base = nextPrimitiveIndex();
3137 index = 0;
3140 definePrimitive(base, index++, "_SCImage_New", prSCImage_New, 4, 0);
3141 //definePrimitive(base, index++, "_SCImage_NewFromFile", prSCImage_NewFromFile, 2, 0); // use only URL
3142 definePrimitive(base, index++, "_SCImage_NewFromURL", prSCImage_NewFromURL, 2, 0);
3143 definePrimitive(base, index++, "_SCImage_WriteToFile", prSCImage_WriteToFile, 3, 0);
3144 definePrimitive(base, index++, "_SCImage_Free", prSCImage_Free, 1, 0);
3145 definePrimitive(base, index++, "_SCImage_setSize", prSCImage_setSize, 3, 0);
3146 definePrimitive(base, index++, "_SCImage_setScalesWhenResized", prSCImage_setScalesWhenResized, 2, 0);
3147 definePrimitive(base, index++, "_SCImage_scalesWhenResized", prSCImage_scalesWhenResized, 1, 0);
3148 definePrimitive(base, index++, "_SCImage_DrawAtPoint", prSCImage_DrawAtPoint, 5, 0);
3149 definePrimitive(base, index++, "_SCImage_DrawInRect", prSCImage_DrawInRect, 5, 0);
3150 definePrimitive(base, index++, "_SCImage_tileInRect", prSCImage_TileInRect, 5, 0);
3151 definePrimitive(base, index++, "_SCImage_lockFocus", prSCImage_lockFocus, 1, 0);
3152 definePrimitive(base, index++, "_SCImage_unlockFocus", prSCImage_unlockFocus, 1, 0);
3153 definePrimitive(base, index++, "_SCImage_recache", prSCImage_recache, 1, 0);
3154 definePrimitive(base, index++, "_SCImage_setPixelAt", prSCImage_setPixelAt, 4, 0);
3155 definePrimitive(base, index++, "_SCImage_getPixelAt", prSCImage_pixelAt, 3, 0);
3156 definePrimitive(base, index++, "_SCImage_setColorAt", prSCImage_setColorAt, 4, 0);
3157 definePrimitive(base, index++, "_SCImage_getColorAt", prSCImage_getColorAt, 3, 0);
3158 definePrimitive(base, index++, "_SCImage_setAccelerated", prSCImage_setAccelerated, 2, 0);
3159 definePrimitive(base, index++, "_SCImage_isAccelerated", prSCImage_isAccelerated, 1, 0);
3160 definePrimitive(base, index++, "_SCImage_loadPixels", prSCImage_loadPixels, 4, 0);
3161 definePrimitive(base, index++, "_SCImage_updatePixels", prSCImage_updatePixels, 3, 0);
3162 definePrimitive(base, index++, "_SCImage_updatePixelsInRect", prSCImage_updatePixelsInRect, 4, 0);
3163 definePrimitive(base, index++, "_SCImage_interpolation", prSCImage_interpolation, 1, 0);
3164 definePrimitive(base, index++, "_SCImage_setInterpolation", prSCImage_setInterpolation, 2, 0);
3165 definePrimitive(base, index++, "_SCImage_sync", prSCImage_sync, 1, 0);
3166 definePrimitive(base, index++, "_SCImage_fromWindowRect", prSCImage_imageFromSCWindowRect, 3, 0);
3168 // image Filter - experimental for now
3169 definePrimitive(base, index++, "_SCImageFilter_NamesInCategory", prSCImageFilter_NamesInCategory, 3, 0);
3170 definePrimitive(base, index++, "_SCImageFilter_Attributes", prSCImageFilter_Attributes, 2, 0);
3171 definePrimitive(base, index++, "_SCImageFilter_Apply", prSCImageFilter_Apply, 4, 0);
3172 definePrimitive(base, index++, "_SCImageFilter_ApplyMultiple", prSCImageFilter_ApplyMultiple, 5, 0);
3173 definePrimitive(base, index++, "_SCImageFilter_GetAttributeMinMax", prSCImageFilter_GetAttributeMinMax, 3, 0);
3174 definePrimitive(base, index++, "_SCImageFilter_ApplyKernel", prSCImageFilter_ApplyKernel, 4, 0);
3175 definePrimitive(base, index++, "_SCImageKernel_Compile", prSCImageKernel_Compile, 1, 0);
3179 #else
3181 void initSCImagePrimitives()
3183 // SCImage not supported for os version < 10.4...
3186 #endif