sc ide: unify font and color configuration panels
[supercollider.git] / editors / scapp / SCImage.M
blob619224133bd10166d35b25cdb36173a4a8f04ad3
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 stringWithCString:path encoding:[NSString defaultCStringEncoding]];
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 SetBool(receiver, result ? true : false); // inform the user if save is ok
1240 return errNone;
1243 int prSCImage_interpolation(struct VMGlobals *g, int numArgsPushed);
1244 int prSCImage_interpolation(struct VMGlobals *g, int numArgsPushed)
1246 if (!g->canCallOS) return errCantCallOS;
1248 PyrSlot *receiver = g->sp; // instance of SCImage
1249 SCImage *image = (SCImage *)slotRawPtr(&slotRawObject(receiver)->slots[0]);
1250 if(!image)
1251 return errNone;
1253 SetInt(receiver, [image imageInterpolation]); //
1254 return errNone;
1257 int prSCImage_setInterpolation(struct VMGlobals *g, int numArgsPushed);
1258 int prSCImage_setInterpolation(struct VMGlobals *g, int numArgsPushed)
1260 if (!g->canCallOS) return errCantCallOS;
1262 PyrSlot *receiver = g->sp - 1; // SCImage
1263 PyrSlot *a = g->sp; // interpolation
1265 int interp = NSImageInterpolationDefault;
1267 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1268 if(!image)
1269 return errNone;
1271 if(slotIntVal(a, &interp) != errNone)
1272 return errFailed;
1274 [image setImageInterpolation:(NSImageInterpolation)interp];
1276 return errNone;
1279 int prSCImage_Free(struct VMGlobals *g, int numArgsPushed);
1280 int prSCImage_Free(struct VMGlobals *g, int numArgsPushed)
1282 if (!g->canCallOS) return errCantCallOS;
1284 PyrSlot *receiver = g->sp; // instance of SCImage
1285 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1286 if(!image)
1287 return errNone;
1289 #if SCIMAGE_DEBUG
1290 NSLog(@"Releasing SCImage (%p)", image);
1291 #endif
1292 [image release];
1294 SetNil(slotRawObject(receiver)->slots + 0); // ensure slot is NIL
1295 SetNil(slotRawObject(receiver)->slots + 1); // not only the pointer
1296 SetNil(slotRawObject(receiver)->slots + 2);
1298 return errNone;
1301 int prSCImage_sync(struct VMGlobals *g, int numArgsPushed);
1302 int prSCImage_sync(struct VMGlobals *g, int numArgsPushed)
1304 if (!g->canCallOS) return errCantCallOS;
1306 PyrSlot *receiver = g->sp; // instance of SCImage
1307 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1308 [image syncRepresentations]; // force sync
1310 #if SCIMAGE_DEBUG
1311 NSLog(@"SCImage (%p) Force Sync", image);
1312 #endif
1313 return errNone;
1316 int prSCImage_setSize(struct VMGlobals *g, int numArgsPushed);
1317 int prSCImage_setSize(struct VMGlobals *g, int numArgsPushed)
1319 if (!g->canCallOS) return errCantCallOS;
1321 PyrSlot *receiver = g->sp - 2; // instance of SCImage
1322 PyrSlot *a = g->sp - 1; // width
1323 PyrSlot *b = g->sp; // height
1325 int err;
1326 float width;
1327 err = slotFloatVal(a, &width);
1328 if (err) return err;
1330 float height;
1331 err = slotFloatVal(b, &height);
1332 if (err) return err;
1334 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1335 [image setSize: NSMakeSize(width, height)];
1337 SetFloat(slotRawObject(receiver)->slots + 1, width);
1338 SetFloat(slotRawObject(receiver)->slots + 2, height);
1340 return errNone;
1343 int prSCImage_setScalesWhenResized(struct VMGlobals *g, int numArgsPushed);
1344 int prSCImage_setScalesWhenResized(struct VMGlobals *g, int numArgsPushed)
1346 if (!g->canCallOS) return errCantCallOS;
1348 PyrSlot *receiver = g->sp - 1; // instance of SCImage
1349 PyrSlot *a = g->sp; // bool flag
1351 BOOL flag = IsTrue(a);
1352 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1353 [image setScalesWhenResized: flag];
1355 return errNone;
1358 int prSCImage_scalesWhenResized(struct VMGlobals *g, int numArgsPushed);
1359 int prSCImage_scalesWhenResized(struct VMGlobals *g, int numArgsPushed)
1361 if (!g->canCallOS) return errCantCallOS;
1363 PyrSlot *receiver = g->sp; // instance of SCImage
1365 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1366 SetBool(receiver, [image scalesWhenResized]);
1367 return errNone;
1370 int prSCImage_DrawAtPoint(struct VMGlobals *g, int numArgsPushed);
1371 int prSCImage_DrawAtPoint(struct VMGlobals *g, int numArgsPushed)
1373 if (!g->canCallOS) return errCantCallOS;
1375 PyrSlot *d = g->sp; // fraction
1376 PyrSlot *c = g->sp - 1; // compositing operation
1377 PyrSlot *b = g->sp - 2; // fromRect
1378 PyrSlot *a = g->sp - 3; // point
1379 PyrSlot *receiver = g->sp - 4; // instance of SCImage
1381 int err;
1382 NSPoint nsPoint;
1384 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1385 if(!image)
1386 return errNone; // just do nothing
1388 if(![image ciimage])
1390 NSLog(@"SCImage CIImage is nil !");
1391 return errFailed;
1394 err = slotGetPoint(a, &nsPoint);
1395 if (err) return err;
1397 NSRect rect;
1398 if (IsNil(b)) {
1399 rect = NSMakeRect(0.0,0.0,[image size].width,[image size].height);
1400 } else {
1401 err = slotGetNSRect(b, &rect);
1402 if (err) return err;
1405 int co;
1406 err = slotIntVal(c, &co);
1407 if (err) return err;
1409 float fraction;
1410 err = slotFloatVal(d, &fraction);
1411 if (err) return err;
1413 // [image drawInSCRect:NSMakeRect(nsPoint.x, nsPoint.y, [image size].width, [image size].height) fromRect:rect operation:(NSCompositingOperation)co fraction:fraction];
1414 [image drawInSCRect:NSMakeRect(nsPoint.x, nsPoint.y, rect.size.width, rect.size.height) fromRect:rect operation:(NSCompositingOperation)co fraction:fraction];
1416 return errNone;
1419 int prSCImage_DrawInRect(struct VMGlobals *g, int numArgsPushed);
1420 int prSCImage_DrawInRect(struct VMGlobals *g, int numArgsPushed)
1422 if (!g->canCallOS) return errCantCallOS;
1424 PyrSlot *d = g->sp; // fraction
1425 PyrSlot *c = g->sp - 1; // compositing operation
1426 PyrSlot *b = g->sp - 2; // fromRect
1427 PyrSlot *a = g->sp - 3; // rect
1428 PyrSlot *receiver = g->sp - 4; // instance of SCImage
1430 int err;
1431 NSRect rect, fromRect;
1433 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1434 if(!image) {
1435 return errNone;
1438 err = slotGetNSRect(a, &rect);
1439 if (err) return err;
1441 if (IsNil(b)) {
1442 fromRect = NSMakeRect(0.0,0.0,[image size].width,[image size].height); // CIImage does not support NSZeroRect as argument
1443 } else {
1444 err = slotGetNSRect(b, &fromRect);
1445 if (err) return err;
1448 int co;
1449 err = slotIntVal(c, &co);
1450 if (err) return err;
1452 float fraction;
1453 err = slotFloatVal(d, &fraction);
1454 if (err) return err;
1456 [image drawInSCRect:rect fromRect:fromRect operation:(NSCompositingOperation)co fraction:fraction];
1458 return errNone;
1461 int prSCImage_TileInRect(struct VMGlobals *g, int numArgsPushed);
1462 int prSCImage_TileInRect(struct VMGlobals *g, int numArgsPushed)
1464 if (!g->canCallOS) return errCantCallOS;
1466 PyrSlot *d = g->sp; // fraction
1467 PyrSlot *c = g->sp - 1; // compositing operation
1468 PyrSlot *b = g->sp - 2; // fromRect
1469 PyrSlot *a = g->sp - 3; // rect
1470 PyrSlot *receiver = g->sp - 4; // instance of SCImage
1472 NSRect nsFromRect, nsRect;
1473 int err, co;
1474 float fraction;
1475 SCImage *image;
1477 if(IsNil(slotRawObject(receiver)->slots+0))
1478 return errNone;
1480 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1481 if(!image) {
1482 return errNone;
1485 err = slotGetNSRect(a, &nsRect);
1486 if (err) return err;
1488 err = slotGetNSRect(b, &nsFromRect);
1489 if (err) return err;
1491 err = slotFloatVal(d, &fraction);
1492 if (err) return err;
1494 err = slotIntVal(c, &co);
1495 if (err) return err;
1497 [image tileInSCRect:nsRect fromRect:nsFromRect operation:(NSCompositingOperation)co fraction:fraction];
1499 return errNone;
1501 int prSCImage_lockFocus(struct VMGlobals *g, int numArgsPushed);
1502 int prSCImage_lockFocus(struct VMGlobals *g, int numArgsPushed)
1504 if (!g->canCallOS) return errCantCallOS;
1506 PyrSlot *receiver = g->sp; // instance of SCImage
1507 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1508 // here we do not use the set flipped because it will create problem
1509 // if we draw on top of an already image loaded with URL and write it after
1510 // better to S(T(ctx)) directly so everything is fine now
1511 // and we can write it with no problem
1512 [image lockFocus];
1513 CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext]graphicsPort];
1514 CGContextSaveGState(ctx);
1515 CGContextTranslateCTM(ctx, 0, [image size].height);
1516 CGContextScaleCTM(ctx, 1, -1.0f);
1518 return errNone;
1521 int prSCImage_unlockFocus(struct VMGlobals *g, int numArgsPushed);
1522 int prSCImage_unlockFocus(struct VMGlobals *g, int numArgsPushed)
1524 if (!g->canCallOS) return errCantCallOS;
1526 PyrSlot *receiver = g->sp; // instance of SCImage
1527 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1528 // revert the coordinate system
1529 CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext]graphicsPort];
1530 CGContextRestoreGState(ctx);
1532 [image unlockFocus];
1534 return errNone;
1537 int prSCImage_recache(struct VMGlobals *g, int numArgsPushed);
1538 int prSCImage_recache(struct VMGlobals *g, int numArgsPushed)
1540 if (!g->canCallOS) return errCantCallOS;
1542 PyrSlot *receiver = g->sp; // instance of SCImage
1544 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1545 [image recache];
1546 return errNone;
1549 // returns a premultipled alpha pixel as an uint32
1550 int prSCImage_pixelAt(struct VMGlobals *g, int numArgsPushed);
1551 int prSCImage_pixelAt(struct VMGlobals *g, int numArgsPushed)
1553 if (!g->canCallOS) return errCantCallOS;
1555 PyrSlot *receiver = g->sp - 2; // instance of SCImage
1556 PyrSlot *xSlot = g->sp - 1;
1557 PyrSlot *ySlot = g->sp;
1559 int x, y, err;
1560 SCImage *image;
1561 PixelData pixelAsArray[4] = {0, 0, 0, 0};
1563 err = errNone;
1565 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1566 if(!image) return errNone;
1568 err = slotIntVal(xSlot, &x);
1569 if(err) return err;
1571 err = slotIntVal(ySlot, &y);
1572 if(err) return err;
1574 [image setAccelerated:NO];
1575 [image getPixel:pixelAsArray atX:x y:y];
1576 //SetInt(receiver, ((pixelAsArray[0] << 24) | (pixelAsArray[1] << 16) | (pixelAsArray[2] << 8) | pixelAsArray[3]));
1577 if( pixelAsArray[3] == 0 ) { // avoid EXC_ARITHMETIC
1578 SetInt(receiver,
1579 (pixelAsArray[0] << 24) |
1580 (pixelAsArray[1] << 16) |
1581 (pixelAsArray[2] << 8)
1583 }else{
1584 SetInt(receiver,
1585 (COLOR_UNSCALE( pixelAsArray[0], pixelAsArray[3] ) << 24) |
1586 (COLOR_UNSCALE( pixelAsArray[1], pixelAsArray[3] ) << 16) |
1587 (COLOR_UNSCALE( pixelAsArray[2], pixelAsArray[3] ) << 8) |
1588 pixelAsArray[3]
1591 return errNone;
1594 int prSCImage_setPixelAt(struct VMGlobals *g, int numArgsPushed);
1595 int prSCImage_setPixelAt(struct VMGlobals *g, int numArgsPushed)
1597 if (!g->canCallOS) return errCantCallOS;
1599 PyrSlot *receiver = g->sp - 3; // instance of SCImage
1600 PyrSlot *pixelSlot = g->sp - 2;
1601 PyrSlot *xSlot = g->sp - 1;
1602 PyrSlot *ySlot = g->sp;
1604 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1605 if(!image)
1606 return errNone;
1608 int x, y, err;
1609 uint32 pixel;
1610 PixelData pixelData[4]={0, 0, 0, 0};
1612 err = slotIntVal(pixelSlot, (int*)&pixel);
1613 if(err) return err;
1615 err = slotIntVal(xSlot, &x);
1616 if(err) return err;
1618 err = slotIntVal(ySlot, &y);
1619 if(err) return err;
1621 [image setAccelerated:NO];
1623 pixelData[0] = COLOR_SCALE(PIXEL_RED(pixel), PIXEL_ALPHA(pixel));
1624 pixelData[1] = COLOR_SCALE(PIXEL_GREEN(pixel), PIXEL_ALPHA(pixel));
1625 pixelData[2] = COLOR_SCALE(PIXEL_BLUE(pixel), PIXEL_ALPHA(pixel));
1626 pixelData[3] = PIXEL_ALPHA(pixel);
1628 [image setPixel:pixelData atX:x y:y];
1630 return errNone;
1633 int prSCImage_setColorAt(struct VMGlobals *g, int numArgsPushed);
1634 int prSCImage_setColorAt(struct VMGlobals *g, int numArgsPushed)
1636 if (!g->canCallOS) return errCantCallOS;
1638 PyrSlot *receiver = g->sp - 3; // instance of SCImage
1639 PyrSlot *colorSlot = g->sp - 2;
1640 PyrSlot *xSlot = g->sp - 1;
1641 PyrSlot *ySlot = g->sp;
1643 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1644 if(!image)
1645 return errNone;
1647 [image setAccelerated:NO];
1649 int x, y, err;
1650 SCColor color;
1652 err = slotColorVal(colorSlot, &color);
1653 if(err) return err;
1655 err = slotIntVal(xSlot, &x);
1656 if(err) return err;
1658 err = slotIntVal(ySlot, &y);
1659 if(err) return err;
1661 [image
1662 setColor:[NSColor colorWithCalibratedRed:color.red green:color.green blue:color.blue alpha:color.alpha]
1663 atX:x
1667 return errNone;
1670 int prSCImage_getColorAt(struct VMGlobals *g, int numArgsPushed);
1671 int prSCImage_getColorAt(struct VMGlobals *g, int numArgsPushed)
1673 if (!g->canCallOS) return errCantCallOS;
1675 PyrSlot *receiver = g->sp - 2; // instance of SCImage
1676 PyrSlot *xSlot = g->sp - 1;
1677 PyrSlot *ySlot = g->sp;
1679 NSColor *nsColor;
1680 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1681 if(!image)
1682 return errNone;
1684 int x, y, err;
1686 err = slotIntVal(xSlot, &x);
1687 if(err) return err;
1689 err = slotIntVal(ySlot, &y);
1690 if(err) return err;
1692 [image setAccelerated:NO];
1694 nsColor = [image
1695 colorAtX:x
1699 if(!nsColor)
1700 SetNil(receiver);
1701 else {
1702 nsColor = [nsColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
1703 PyrObject *color = instantiateObject(g->gc, s_color->u.classobj, 0, false, true);
1704 PyrSlot *slots = color->slots;
1705 SetFloat(slots+0, [nsColor redComponent]);
1706 SetFloat(slots+1, [nsColor greenComponent]);
1707 SetFloat(slots+2, [nsColor blueComponent]);
1708 SetFloat(slots+3, [nsColor alphaComponent]);
1709 SetObject(receiver, color);
1712 return errNone;
1715 int prSCImage_loadPixels(struct VMGlobals *g, int numArgsPushed);
1716 int prSCImage_loadPixels(struct VMGlobals *g, int numArgsPushed)
1718 if (!g->canCallOS) return errCantCallOS;
1720 PyrSlot *receiver = g->sp - 3;
1721 PyrSlot *pixelSlot = g->sp - 2;
1722 PyrSlot *regionSlot= g->sp - 1;
1723 PyrClass *pyrclass;
1725 uint32 *pixelsData;
1726 NSBitmapImageRep *bitmap;
1727 SCImage *image;
1728 int height, width, y=0, x=0, startIndex=0;
1729 NSSize size;
1730 NSRect region;
1732 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1733 if(!image){
1734 SetNil(receiver);
1735 return errNone;
1737 size = [image size];
1739 pyrclass = slotRawObject(pixelSlot)->classptr;
1740 if(pyrclass != class_int32array) {
1741 NSLog(@"prSCImage_loadPixels array argument is not a PyrInt32Array !");
1742 return errFailed;
1745 slotIntVal(g->sp, &startIndex);
1746 if(startIndex < 0)
1747 return errIndexOutOfRange;
1749 PyrInt32Array* allocatedPixelArray = (PyrInt32Array*)slotRawObject(pixelSlot);
1751 bitmap = [image bitmapRepresentation];
1753 if(IsNil(regionSlot)) {
1754 region = NSIntegralRect(NSMakeRect(0.f, 0.f, [bitmap pixelsWide], [bitmap pixelsHigh]));
1755 }else{
1756 if(slotGetNSRect(regionSlot, &region) != errNone)
1757 return errWrongType;
1758 region = NSIntegralRect(region);
1759 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]) )
1760 return errIndexOutOfRange;
1763 height = region.size.height;
1764 width = region.size.width;
1765 pixelsData = (uint32*)allocatedPixelArray->i + startIndex;
1766 PixelData pixel[4]={0, 0, 0, 0};
1768 if((allocatedPixelArray->size - startIndex) < ( height * width /** 4*/ ) ) {
1769 post("Error: pixel array is not of good size ! should be %i bytes long\n", height * width /** 4*/);
1770 return errIndexOutOfRange;
1773 [image lock];
1774 unsigned char alpha;
1775 height = height + region.origin.y; //padding h and w to meet origin
1776 width = width + region.origin.x;
1777 for(y=(int)region.origin.y; y < height; y++) {
1778 for(x=(int)region.origin.x; x < width; x++) {
1780 // most of the unscale routines may be not as precise as we need
1781 // especially when dealing with CIImage <-> NSImage conversion...
1782 // it should be better to use vImage framework directly !
1784 [bitmap getPixel:pixel atX:x y:y];
1785 if(alpha > 0) {
1786 SET_PIXEL_RED ( *pixelsData, COLOR_UNSCALE(pixel[0], pixel[3]) );
1787 SET_PIXEL_GREEN( *pixelsData, COLOR_UNSCALE(pixel[1], pixel[3]) );
1788 SET_PIXEL_BLUE ( *pixelsData, COLOR_UNSCALE(pixel[2], pixel[3]) );
1789 SET_PIXEL_ALPHA( *pixelsData, pixel[3]);
1790 }else{
1791 SET_PIXEL_RED ( *pixelsData, pixel[0] );
1792 SET_PIXEL_GREEN( *pixelsData, pixel[1] );
1793 SET_PIXEL_BLUE ( *pixelsData, pixel[2] );
1794 SET_PIXEL_ALPHA( *pixelsData, pixel[3] );
1796 ++pixelsData;
1798 //bitmapData += bytesLeft;
1800 [image unlock];
1802 return errNone;
1805 int prSCImage_updatePixels(struct VMGlobals *g, int numArgsPushed);
1806 int prSCImage_updatePixels(struct VMGlobals *g, int numArgsPushed)
1808 if (!g->canCallOS) return errCantCallOS;
1810 PyrSlot *receiver = g->sp - 2;
1811 PyrSlot *pixelSlot = g->sp - 1;
1812 SCImage *image;
1814 NSBitmapImageRep *bitmap;
1815 int width, height, bytesPerRow, bytesLeft, startIndex=0, y=0, x=0;
1816 unsigned char *bitmapData;
1817 uint32 *pixelsData;
1818 PyrInt32Array *pixels;
1819 PyrClass *pyrclass;
1821 if(IsNil(pixelSlot))
1822 return errNone;
1824 slotIntVal(g->sp, &startIndex);
1825 if(startIndex < 0)
1826 return errIndexOutOfRange;
1828 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1829 if(!image){
1830 SetNil(receiver);
1831 return errNone;
1834 pyrclass = slotRawObject(pixelSlot)->classptr;
1835 if(pyrclass != class_int32array) {
1836 NSLog(@"prSCImage_loadPixels array argument is not a PyrInt32Array !");
1837 return errFailed;
1840 pixels = (PyrInt32Array*)slotRawObject(pixelSlot);
1842 bitmap = [image bitmapRepresentation];
1843 if(!bitmap) {
1844 NSLog(@"prSCImage_updatePixels failed retrieving valid NSBitmapImageRep...");
1845 return errFailed;
1848 width = [bitmap pixelsWide]; // RGBA
1849 height = [bitmap pixelsHigh];
1850 bytesPerRow = [bitmap bytesPerRow];
1851 bitmapData = [bitmap bitmapData];
1852 pixelsData = (uint32*)pixels->i + startIndex;
1853 bytesLeft = bytesPerRow - (width*4);
1855 if( (pixels->size - startIndex) < (int)(width*height /* *4 */)) {
1856 post("Error: pixel array is not of good size ! should be %i bytes long from index: %i\n", (int)(width*height/**4*/), startIndex);
1857 return errIndexOutOfRange;
1860 [image lock];
1861 unsigned char alpha;
1862 for(y=0; y < height; ++y) {
1863 for(x=0; x < width; ++x) {
1864 alpha = PIXEL_ALPHA(*pixelsData);
1865 *bitmapData++ = COLOR_SCALE(PIXEL_RED(*pixelsData), alpha);
1866 *bitmapData++ = COLOR_SCALE(PIXEL_GREEN(*pixelsData), alpha);
1867 *bitmapData++ = COLOR_SCALE(PIXEL_BLUE(*pixelsData), alpha);
1868 *bitmapData++ = alpha;
1869 ++pixelsData;
1871 bitmapData += bytesLeft;
1873 [image unlock];
1875 [image setShouldSyncCIImage:YES];
1877 return errNone;
1881 int prSCImage_updatePixelsInRect(struct VMGlobals *g, int numArgsPushed);
1882 int prSCImage_updatePixelsInRect(struct VMGlobals *g, int numArgsPushed)
1884 if (!g->canCallOS) return errCantCallOS;
1886 PyrSlot *receiver = g->sp - 3;
1887 PyrSlot *pixelSlot = g->sp - 2;
1888 PyrSlot *rectSlot = g->sp - 1;
1889 SCImage *image;
1891 NSBitmapImageRep *bitmap;
1892 int width, height, bytesPerRow, bytesLeft, y=0, x=0, startIndex=0, err;
1893 unsigned char *bitmapData;
1894 uint32 *pixelsData;
1895 PyrInt32Array *pixels;
1896 PyrClass *pyrclass;
1897 NSRect rect;
1899 if(IsNil(pixelSlot))
1900 return errNone;
1902 slotIntVal(g->sp, &startIndex); // get the Array start index
1903 if(startIndex < 0)
1904 return errIndexOutOfRange;
1906 image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1907 if(!image){
1908 SetNil(receiver);
1909 return errNone;
1912 err = slotGetNSRect(rectSlot, &rect);
1913 if (err) return err;
1914 rect = NSIntegralRect(rect);
1916 pyrclass = slotRawObject(pixelSlot)->classptr;
1917 if(pyrclass != class_int32array) {
1918 NSLog(@"prSCImage_loadPixels array argument is not a PyrInt32Array !");
1919 return errFailed;
1922 pixels = (PyrInt32Array*)slotRawObject(pixelSlot);
1923 bitmap = [image bitmapRepresentation];
1924 if(!bitmap) {
1925 NSLog(@"prSCImage_updatePixels failed retrieving valid NSBitmapImageRep...");
1926 return errIndexOutOfRange;
1929 width = [bitmap pixelsWide]; // RGBA
1930 height = [bitmap pixelsHigh];
1931 bytesPerRow = [bitmap bytesPerRow];
1932 bitmapData = [bitmap bitmapData];
1933 pixelsData = (uint32*)pixels->i + startIndex;
1935 // clipping
1936 rect.origin.x = sc_max(rect.origin.x, 0); // negative idx not allowed
1937 rect.origin.y = sc_max(rect.origin.y, 0); // negative idx not allowed
1938 rect.size.width = sc_min(rect.size.width, width); // clip to image width max
1939 rect.size.height = sc_min(rect.size.height, height); // clip to image height max
1941 // what is the best :
1942 // 1 - clipping + wrapping thus giving strange results ?
1943 // 2 - throw an error ?
1944 // just throw an error for now -> so no strange results and no scratching head... :)
1945 // zero tolerance >:^(
1947 ((rect.origin.x + rect.size.width) > width) ||
1948 ((rect.origin.y + rect.size.height) > height)
1950 return errIndexOutOfRange;
1952 bytesLeft = (bytesPerRow - (rect.size.width * 4));
1953 if( (pixels->size - startIndex) < (int)(rect.size.width * rect.size.height)) {
1954 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);
1955 return errNone;
1958 [image lock];
1959 unsigned char alpha;
1960 bitmapData += ((int)rect.origin.y * bytesPerRow) + ((int)rect.origin.x * 4);
1961 for(y=0; y < rect.size.height; ++y) {
1962 for(x=0; x < rect.size.width; ++x) {
1963 alpha = PIXEL_ALPHA(*pixelsData);
1964 *bitmapData++ = COLOR_SCALE(PIXEL_RED(*pixelsData), alpha);
1965 *bitmapData++ = COLOR_SCALE(PIXEL_GREEN(*pixelsData), alpha);
1966 *bitmapData++ = COLOR_SCALE(PIXEL_BLUE(*pixelsData), alpha);
1967 *bitmapData++ = alpha;
1968 ++pixelsData;
1970 bitmapData += bytesLeft;
1972 [image unlock];
1974 [image setShouldSyncCIImage:YES];
1976 return errNone;
1979 int prSCImage_isAccelerated(struct VMGlobals *g, int numArgsPushed);
1980 int prSCImage_isAccelerated(struct VMGlobals *g, int numArgsPushed)
1982 if (!g->canCallOS) return errCantCallOS;
1984 PyrSlot *receiver = g->sp;
1986 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
1987 if(!image){
1988 SetNil(receiver);
1989 return errNone;
1992 SetBool(receiver, [image isAccelerated]);
1994 return errNone;
1997 int prSCImage_setAccelerated(struct VMGlobals *g, int numArgsPushed);
1998 int prSCImage_setAccelerated(struct VMGlobals *g, int numArgsPushed)
2000 if (!g->canCallOS) return errCantCallOS;
2002 PyrSlot *receiver = g->sp - 1;
2003 PyrSlot *yornSlot = g->sp;
2005 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
2006 if(!image){
2007 SetNil(receiver);
2008 return errNone;
2011 [image setAccelerated:IsTrue(yornSlot)];
2013 return errNone;
2017 @class SCGraphView;
2018 // Currently it grabs a portion from the scgraphview
2019 // user should here pass the ABSOLUTE view bounds or a specific rect
2020 int prSCImage_imageFromSCWindowRect(struct VMGlobals *g, int numArgsPushed);
2021 int prSCImage_imageFromSCWindowRect(struct VMGlobals *g, int numArgsPushed)
2023 if (!g->canCallOS) return errCantCallOS;
2025 PyrSlot *receiver = g->sp - 2; // SCImage class
2026 PyrSlot *a = g->sp - 1; // SCWindow instance
2027 PyrSlot *b = g->sp; // SCRect instance - portion in the SCGraphView
2029 NSRect rect;
2030 NSBitmapImageRep *nsbitmap = nil;
2031 NSImage *nsimage = nil;
2032 SCGraphView *nsview = nil;
2033 SCImage *scimage = nil;
2034 int err = errNone;
2036 if(!IsPtr(&slotRawObject(a)->slots[0])) {
2037 return errFailed;
2040 nsview = (SCGraphView*)slotRawPtr(&slotRawObject(a)->slots[0]); // get SCGraphView instance
2041 err = slotGetNSRect(b, &rect);
2042 if (err)
2043 return err;
2045 [nsview lockFocus];
2046 nsbitmap = [[NSBitmapImageRep alloc]initWithFocusedViewRect:rect];
2047 [nsview unlockFocus];
2049 if(!nsbitmap)
2051 post("Error: Failed getting valid image from SCView !\n");
2053 else
2055 PyrObject* object = NULL;
2057 nsimage =
2058 [[NSImage alloc]initWithSize:NSMakeSize((float)[nsbitmap pixelsWide], (float)[nsbitmap pixelsHigh])];
2060 if(!nsimage) {
2061 post("Error: Failed creating valid NSImage !\n");
2062 SetNil(receiver);
2063 goto Bail;
2066 [nsimage addRepresentation:nsbitmap];
2068 scimage = [[SCImage alloc]initWithNSImage:nsimage];
2069 if(!scimage) {
2070 post("Error: Failed creating valid SCImage !\n");
2071 SetNil(receiver);
2072 return errNone;
2075 // Instantiate and set the SCObject
2076 object = newPyrSCImage(g);
2077 if(!object) {
2078 post("Error: Failed creating valid SCImage class !");
2079 [scimage release];
2080 SetNil(receiver);
2081 goto Bail;
2084 SetPtr(object->slots + 0, scimage);
2085 SetFloat(object->slots + 1, (float)[nsbitmap pixelsWide]);
2086 SetFloat(object->slots + 2, (float)[nsbitmap pixelsHigh]);
2087 SetObject(receiver, object);
2090 Bail:
2091 if(nsimage) [nsimage release];
2092 if(nsbitmap) [nsbitmap release];
2094 return errNone;
2097 #pragma mark --- SCImageFilter Primitive Set
2099 PyrClass* slotArrayGetCommonClass(PyrObject* array)
2101 int size = array->size;
2102 if(size == 0)
2103 return NULL;
2105 PyrClass *commonClass = slotRawObject(&array->slots[0])->classptr;
2107 for(int i=1; i < size; ++i) {
2108 if(slotRawObject(&array->slots[0])->classptr != commonClass)
2109 return NULL;
2112 return commonClass;
2115 BOOL nsClassIsKindOfClass(Class currentClass, Class match);
2116 BOOL nsClassIsKindOfClass(Class currentClass, Class match)
2118 if(currentClass == match)
2119 return YES;
2121 Class superClass;
2122 while( (superClass = class_getSuperclass(currentClass)) ) {
2123 if(superClass == match)
2124 return YES;
2127 return NO;
2130 id ciVectorFromSlot(PyrObject* slot);
2131 id ciVectorFromSlot(PyrObject* slot)
2133 ColorData values[4] = {0.f, 0.f, 0.f, 0.f};
2134 int size = slot->size, err;
2136 for (int i=0; i < size; ++i) {
2137 float val;
2138 err = slotFloatVal(slot->slots + i, &val);
2139 if(err) return nil;
2140 values[i] = val;
2143 return [CIVector vectorWithValues:values count:(size_t)4];
2146 int safeSlotFloatVal(PyrSlot* slot, float* value);
2147 int safeSlotFloatVal(PyrSlot* slot, float* value)
2149 if(IsNil(slot)) {
2150 *value = 0.f;
2151 return errNone;
2153 return slotFloatVal(slot, value);
2156 CGRect OutsetRect(CGRect r, const float dx, const float dy)
2158 CGRect ret = CGRectMake(r.origin.x - dx, r.origin.y - dy, r.size.width + dx + dx, r.size.height + dy + dy);
2159 return ret;
2162 CIImage* CropAndCenterCIImage(CIImage *image, CGRect originalExtent, CGRect* cropRegionRef);
2163 CIImage* CropAndCenterCIImage(CIImage *image, CGRect originalExtent, CGRect* cropRegionRef)
2165 #if SCIMAGE_FILTER_DEBUG
2166 NSLog(@"Cropping and centering CIImage %p", image);
2167 #endif
2168 CGRect extent = [image extent];
2169 CIFilter * ciFilter;
2171 // cropping
2172 if(cropRegionRef)
2174 CGRect cropRegion = *cropRegionRef;
2175 // Crop to specified rectangle.
2176 if(extent.size.width > cropRegion.size.width || extent.size.height > cropRegion.size.height)
2178 if(extent.origin.x > 0) {cropRegion.origin.x = extent.origin.x;}
2179 if(extent.origin.y > 0) {cropRegion.origin.y = extent.origin.y;}
2182 // quite strange but happens sometime...
2183 if(extent.origin.x < 0 && ((extent.origin.x + extent.size.width) < cropRegion.size.width)) {
2184 float diff = extent.size.width + extent.origin.x;
2185 cropRegion.origin.x -= (cropRegion.size.width - diff);
2188 if(extent.origin.y < 0 && ((extent.origin.y + extent.size.height) < cropRegion.size.height)) {
2189 float diff = extent.size.height + extent.origin.y;
2190 cropRegion.origin.y -= (cropRegion.size.height - diff);
2193 #if SCIMAGE_FILTER_DEBUG
2194 NSLog(@"SCImageFilter: Cropping To Size: (%4.2f, %4.2f, %4.2f, %4.2f) fromExtent: (%4.2f, %4.2f, %4.2f, %4.2f)",
2195 cropRegion.origin.x,cropRegion.origin.y,cropRegion.size.width,cropRegion.size.height,
2196 extent.origin.x,extent.origin.y,extent.size.width,extent.size.height
2198 #endif
2200 ciFilter = [CIFilter filterWithName:@"CICrop"]; // crop the final result according
2201 [ciFilter setValue:image forKey:@"inputImage"];
2202 [ciFilter setValue:[CIVector vectorWithX:cropRegion.origin.x Y:cropRegion.origin.y Z:cropRegion.size.width W:cropRegion.size.height] forKey:@"inputRectangle"];
2203 image = [ciFilter valueForKey:@"outputImage"];
2204 extent = [image extent];
2205 #if SCIMAGE_FILTER_DEBUG
2206 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);
2207 #endif
2211 // translating origin and center it if we need it
2212 // currently auto translation is now deactivated by default - let the user choose
2215 if(extent.origin.x != 0 || extent.origin.y != 0) {
2216 ciFilter = [CIFilter filterWithName:@"CIAffineTransform"];
2217 NSAffineTransform* trs = [NSAffineTransform transform];
2218 [trs translateXBy:-extent.origin.x yBy:-extent.origin.y]; // move to zero
2219 [ciFilter setValue:trs forKey:@"inputTransform"];
2220 [ciFilter setValue:image forKey:@"inputImage"];
2221 image = [ciFilter valueForKey:@"outputImage"];
2222 #if SCIMAGE_FILTER_DEBUG
2223 NSLog(@"SCImageFilter: After Translate: (%4.2f, %4.2f, %4.2f, %4.2f)", extent.origin.x,extent.origin.y,extent.size.width, extent.size.height);
2224 #endif
2228 return image;
2231 id nsciObjectFromSlot(PyrSlot *slot, Class attributeClass, NSString *attributeType);
2232 id nsciObjectFromSlot(PyrSlot *slot, Class attributeClass, NSString *attributeType)
2234 int err;
2235 if(attributeClass == [CIImage class] || attributeType == kCIAttributeTypeGradient)
2237 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(slot)->slots);
2238 return [image ciimage];
2240 else if(isKindOfSlot(slot, s_scimage->u.classobj) && (attributeClass == NULL) && (attributeType == NULL)) // here returns the CISampler by default
2242 SCImage *image = (SCImage *)slotRawPtr(slotRawObject(slot)->slots);
2243 return [CISampler samplerWithImage:[image ciimage]];
2245 // else if(isKindOfSlot(slot, s_vector->u.classobj)) {
2247 else if(isKindOfSlot(slot, s_array->u.classobj)) {
2248 #if SCIMAGE_FILTER_DEBUG
2249 NSLog(@"Creating CIVector From Vector Slot");
2250 #endif
2252 return ciVectorFromSlot(slotRawObject(slot));
2255 else if(isKindOfSlot(slot, s_color->u.classobj))
2257 PyrSlot* color_slots = slotRawObject(slot)->slots;
2258 float cr,cg,cb,ca;
2259 err = slotFloatVal(color_slots+0, &cr);
2260 if (err) cr = 0.f;
2261 err = slotFloatVal(color_slots+1, &cg);
2262 if (err) cg = 0.f;
2263 err = slotFloatVal(color_slots+2, &cb);
2264 if (err) cb = 0.f;
2265 err = slotFloatVal(color_slots+3, &ca);
2266 if (err) ca = 1.f;
2268 #if SCIMAGE_FILTER_DEBUG
2269 NSLog(@"Creating CIColor/NSColor From Color Slot");
2270 #endif
2272 if(attributeClass == [NSColor class])
2274 return [NSColor colorWithCalibratedRed:cr green:cg blue:cb alpha:ca];
2276 return [CIColor colorWithRed:cr green:cg blue:cb alpha:ca];
2278 else if(isKindOfSlot(slot, s_array->u.classobj))
2280 if(attributeClass == [NSAffineTransform class]) {
2281 #if SCIMAGE_FILTER_DEBUG
2282 NSLog(@"Creating NSAffineTransform From Array Slot");
2283 #endif
2284 // array containing [Tx, Ty, Sx, Sy, Rot in radians]
2285 PyrObject* array = slotRawObject(slot);
2286 int size = sc_min(array->size, 5), err;
2287 float attributes[5] = {0.f, 0.f, 1.f, 1.f, 0.f};
2288 for(int i=0; i < size; ++i) {
2289 err = slotFloatVal(array->slots+i, attributes+i);
2290 if(err)
2291 attributes[i] = 0.f;
2294 NSAffineTransform *transform = [NSAffineTransform transform];
2295 [transform translateXBy:attributes[0] yBy:attributes[1]];
2296 [transform scaleXBy:attributes[2] yBy:attributes[3]];
2297 [transform rotateByRadians:attributes[4]];
2298 return transform;
2301 if(attributeClass == [CIVector class] || attributeClass == NULL) {
2302 #if SCIMAGE_FILTER_DEBUG
2303 NSLog(@"Creating CIVector From Array Slot");
2304 #endif
2305 return ciVectorFromSlot(slotRawObject(slot));
2308 #if 0
2309 if(nsClassIsKindOfClass(attributeClass, [NSData class])) {
2310 // handle specific case here...
2311 // mostly for CIColorCube Filter
2312 #if SCIMAGE_FILTER_DEBUG
2313 NSLog(@"Creating NSData From Array Slot");
2314 #endif
2315 PyrClass *support = slotArrayGetCommonClass(slotRawObject(slot));
2317 support == NULL ||
2320 support != s_int8array->u.classobj &&
2321 support != s_int16array->u.classobj &&
2322 support != s_int32array->u.classobj &&
2323 support != s_doublearray->u.classobj &&
2324 support != class_floatarray->u.classobj
2328 return nil;
2331 #endif
2333 else if(IsTrue(slot) || IsFalse(slot) )
2335 #if SCIMAGE_FILTER_DEBUG
2336 NSLog(@"Creating NSNumber From Boolean Slot");
2337 #endif
2338 return [NSNumber numberWithBool:(IsTrue(slot))];
2340 else if(IsFloat(slot) || IsInt(slot))
2342 #if SCIMAGE_FILTER_DEBUG
2343 NSLog(@"Creating NSNumber From Float/Int Slot");
2344 #endif
2346 float val;
2347 err = safeSlotFloatVal(slot, &val);
2348 if(err) return nil;
2350 if(attributeType == kCIAttributeTypeBoolean) {
2351 return [NSNumber numberWithBool:val > 0];
2353 return [NSNumber numberWithFloat:val];
2356 post("Non supported type of class: %s", slotRawObject(slot)->classptr->name);
2357 return nil; // failure
2361 int prSCImageFilter_ApplyMultiple(struct VMGlobals *g, int numArgsPushed);
2362 int prSCImageFilter_ApplyMultiple(struct VMGlobals *g, int numArgsPushed)
2364 PyrSlot *receiver = g->sp - 4;
2365 PyrSlot *filtersSlot = g->sp - 3;
2366 PyrSlot *inPlace = g->sp - 2;
2367 // PyrSlot *updateRegionSlot = g->sp - 1;
2368 PyrSlot *cropRegionSlot = g->sp;
2369 int err = errNone;
2371 SCImage *scimage = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
2372 if(!scimage){
2373 SetNil(receiver);
2374 return errNone;
2377 int numberOfFilters;
2378 PyrObject* scFilter;
2379 PyrObject *arguments;
2380 PyrSymbol *key;
2381 PyrSlot *value;
2382 id nsvalue;
2383 //NSArray *inputKeys;
2384 NSString *nskey;
2385 Class valueClass;
2386 CIImage* ciimage;
2387 CIFilter* ciFilter;
2388 CGRect origExtent = [scimage extent];
2389 CGRect destExtent;
2390 //CGPoint delta;
2392 numberOfFilters = slotRawObject(filtersSlot)->size;
2394 // Get the CIImage rep in the SCImage
2395 if(![scimage isAccelerated])
2396 [scimage syncCIImage]; // update ciimage rep
2397 ciimage = [scimage ciimage]; // now we can get it
2399 // Iterate through all the filters
2400 for(int i=0; i < numberOfFilters; ++i)
2402 int size = 0;
2404 scFilter = slotRawObject(&(slotRawObject(filtersSlot)->slots[i]));
2406 if(IsFalse(scFilter->slots+3)) // filter enabled ?
2407 continue;
2409 ciFilter = [CIFilter filterWithName: nsStringFromSlot(scFilter->slots+0)]; // filter name
2410 if(!ciFilter) return errFailed;
2411 #if SCIMAGE_FILTER_DEBUG
2412 NSLog(@"SCImageFilter --- Setting Filter Named %@", nsStringFromSlot(scFilter->slots+0));
2413 #endif
2414 [ciFilter setDefaults];
2416 arguments = slotRawObject(&scFilter->slots[2]); // filter values
2417 size = (arguments->size >> 1);
2419 for(int i=0; i < size; ++i)
2421 key = slotRawSymbol(arguments->slots + (i<<1));
2422 value = arguments->slots + ((i<<1) + 1);
2423 nskey = [NSString stringWithFormat:@"input%c%s", toupper(*key->name), key->name+1];
2424 valueClass = NSClassFromString( [[[ciFilter attributes] objectForKey:nskey] objectForKey:@"CIAttributeClass"]);
2425 nsvalue = nsciObjectFromSlot(value, valueClass, [[ciFilter attributes] objectForKey:kCIAttributeType]);
2427 if(nsvalue)
2429 #if SCIMAGE_FILTER_DEBUG
2430 NSLog(@"Setting Key: %@ Value: %@ Class: %@", nskey, nsvalue, valueClass);
2431 #endif
2432 [ciFilter setValue:nsvalue forKey:nskey];
2434 else
2436 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));
2440 // concatenate filters
2441 if([[ciFilter inputKeys]containsObject:@"inputImage"])
2443 [ciFilter setValue:ciimage forKey:@"inputImage"];
2446 // get results
2447 ciimage = [ciFilter valueForKey:@"outputImage"];
2448 ////
2451 destExtent = [ciimage extent];
2453 #if SCIMAGE_MAC_OS_10_5
2455 //CGRectIsInfinite(destExtent) || // infinite rects are mosly results from generator filters
2456 !IsNil(updateRegionSlot) // better to keep original extent if constrained update
2459 destExtent = origExtent;
2461 else
2463 #endif
2464 // if the rect is not infinite :
2465 // 1 - crop the image if a cropRegion is specified
2466 // 2 - translate the image to align origin of extent with coordindate origin of image (0,0)
2467 CGRect cropRegion;
2468 if(IsNil(cropRegionSlot)) {
2469 #if SCIMAGE_FILTER_DEBUG
2470 NSLog(@"SCImageFilter: Nil Crop Region - creating from %i, %i maximum crop size", SCCIIMAGE_MAX_WIDTH, SCCIIMAGE_MAX_HEIGHT);
2471 #endif
2472 cropRegion = OutsetRect(origExtent, floorf((SCCIIMAGE_MAX_WIDTH - origExtent.size.width) * 0.5), floorf((SCCIIMAGE_MAX_HEIGHT - origExtent.size.height) * 0.5));
2473 } else {
2474 err = slotGetNSRect(cropRegionSlot, (NSRect*)&cropRegion);
2475 if(err) return err;
2477 ciimage = CropAndCenterCIImage(ciimage, origExtent, &cropRegion);
2478 destExtent = [ciimage extent];
2480 #if SCIMAGE_MAC_OS_10_5
2482 #endif
2485 PyrObject *receiverObj;
2486 if(IsTrue(inPlace))
2488 // Here the caller should be directly updated !
2489 // Size will be equal if :
2490 // 1 - the filter does not extend the image bounds (modified / infinite extent)
2491 // 2 - the update region slot if not nil to constrain updates
2492 if(CGSizeEqualToSize(destExtent.size, origExtent.size))
2494 #if SCIMAGE_MAC_OS_10_5
2495 if(!IsNil(updateRegionSlot))
2497 CGRect updateRegion;
2498 err = slotGetNSRect(updateRegionSlot, (NSRect*)&updateRegion);
2499 if(err) return err;
2500 // Invert SC Coordinates
2501 updateRegion.origin.y = [[scimage accumulator]extent].size.height - (updateRegion.origin.y + updateRegion.size.height);
2502 [[scimage accumulator] setImage:ciimage dirtyRect:updateRegion];
2504 else
2506 [[scimage accumulator] setImage:ciimage];
2508 #else
2509 [ciimage retain];
2510 [scimage->_ciimage release];
2511 scimage->_ciimage = ciimage;
2512 #endif
2513 [scimage setShouldSyncBitmap:YES]; // keep the sync info
2514 return errNone;
2516 else
2518 receiverObj = slotRawObject(receiver);
2520 #if SCIMAGE_FILTER_DEBUG
2521 NSLog(@"SCImageFilter: New Extent (%4.2f, %4.2f, %4.2f, %4.2f)", destExtent.origin.x, destExtent.origin.y, destExtent.size.width, destExtent.size.height);
2522 #endif
2524 else
2526 receiverObj = newPyrSCImage(g);
2527 if(!receiverObj)
2529 post("Failed creating valid SCImage class !");
2530 return errFailed;
2534 #if SCIMAGE_DEBUG || SCIMAGE_FILTER_DEBUG
2535 NSLog(@"SCImageFilter: Creating New SCImage Class");
2536 NSLog(@"SCImageFilter: OriginalImage Extent: (%4.2f, %4.2f, %4.2f, %4.2f) NewImage Extent: (%4.2f, %4.2f, %4.2f, %4.2f)",
2537 origExtent.origin.x, origExtent.origin.y, origExtent.size.width, origExtent.size.height,
2538 destExtent.origin.x,destExtent.origin.y,destExtent.size.width, destExtent.size.height
2540 #endif
2542 SCImage *newSCImage = [[SCImage alloc]initWithCIImage:ciimage extent:destExtent format:[scimage format]];
2543 if(!newSCImage)
2545 post("Error: Failed creating valid CIImage cache !!!");
2546 return errFailed;
2549 value = receiverObj->slots;
2550 SetPtr(value + 0, newSCImage);
2551 SetFloat(value + 1, destExtent.size.width);
2552 SetFloat(value + 2, destExtent.size.height);
2554 if(receiverObj == slotRawObject(receiver))
2555 [scimage release];
2556 else
2557 SetObject(receiver, receiverObj);
2558 return errNone;
2562 int prSCImageFilter_Apply(struct VMGlobals *g, int numArgsPushed);
2563 int prSCImageFilter_Apply(struct VMGlobals *g, int numArgsPushed)
2565 PyrSlot *receiver = g->sp - 3;
2566 PyrSlot *filterNameSlot = g->sp - 2;
2567 PyrSlot *argumentsSlot = g->sp - 1;
2568 PyrSlot *inPlace = g->sp;
2570 SCImage *scimage = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
2571 if(!scimage){
2572 SetNil(receiver);
2573 return errNone;
2576 CIFilter* ciFilter = [CIFilter filterWithName: nsStringFromSlot(filterNameSlot)];
2577 if(!ciFilter)
2578 return errFailed;
2580 [ciFilter setDefaults];
2582 PyrObject *arguments = slotRawObject(argumentsSlot); // array
2583 PyrSymbol *key;
2584 PyrSlot *value;
2585 NSString *nskey;
2586 Class valueClass;
2587 id nsvalue;
2589 int size = (arguments->size >> 1);
2591 for(int i=0; i < size; ++i) {
2592 key = (PyrSymbol*)slotRawSymbol(arguments->slots + (i<<1));
2593 value = arguments->slots + ((i<<1) + 1);
2594 nskey = [NSString stringWithFormat:@"input%c%s", toupper(*key->name), key->name+1];
2595 valueClass = NSClassFromString( [[[ciFilter attributes] objectForKey:nskey] objectForKey:@"CIAttributeClass"]);
2596 nsvalue = nsciObjectFromSlot(value, valueClass, [[ciFilter attributes] objectForKey:kCIAttributeType]);
2597 if(nsvalue) {
2598 #if SCIMAGE_FILTER_DEBUG
2599 NSLog(@"Setting Key: %@ Value: %@ Class: %@", nskey, nsvalue, valueClass);
2600 #endif
2601 [ciFilter setValue:nsvalue forKey:nskey];
2602 }else{
2603 post("Error: bad (nil) slot value for key %s, NSObject conversion failed for class %s !", [nskey UTF8String], class_getName(valueClass));
2607 if(IsTrue(inPlace)) {
2608 [scimage applyFilter:ciFilter]; // in place filter
2610 else {
2611 #if SCIMAGE_DEBUG
2612 NSLog(@"Creating New SCImage Class");
2613 #endif
2615 scimage = [scimage imageFilteredWith:ciFilter]; // filtered copy
2616 PyrObject* object = newPyrSCImage(g);
2617 PyrSlot *slots = object->slots;
2618 SetPtr(slots + 0, scimage);
2619 SetFloat(slots + 1, (float)[scimage width]);
2620 SetFloat(slots + 2, (float)[scimage height]);
2621 SetObject(receiver, object);
2624 return errNone;
2629 * Currently only cache the CIKernel object + install a finalizer on it
2631 int cachePyrKernel(VMGlobals *g, PyrObject* kernelObject)
2633 NSString* kernelCmd = nsStringFromSlot(kernelObject->slots+0);
2634 if(!kernelCmd) {
2635 post("Error: Kernel string command does not seem valid !\n");
2636 return errFailed;
2639 id kernel = [CIKernel kernelsWithString:kernelCmd];
2640 if(!kernel) {
2641 post("Error: Failed creating valid Kernel from shader string !!\n");
2642 return errFailed;
2645 // Create the Kernel
2646 kernel = [(NSArray*)kernel objectAtIndex:0];
2647 if(!kernel) {
2648 post("Error: Failed retrieving valid CIKernel !");
2649 return errFailed;
2651 [kernel retain];
2653 if( !IsNil(kernelObject->slots+4) ) {
2654 id current = (id)slotRawPtr(&kernelObject->slots[4]);
2655 if(current)
2656 [current release];
2659 SetPtr(kernelObject->slots+4, kernel);
2661 if( IsNil(kernelObject->slots+5) ) {
2662 #if SCIMAGE_DEBUG
2663 post("install finalizer scimagekernel (%p)\n", kernelObject);
2664 #endif
2665 InstallFinalizer(g, kernelObject, 5, FinalizeSCImageKernelObject);
2667 #if SCIMAGE_DEBUG
2668 post("caching scimagekernel (%p)\n", kernelObject);
2669 #endif
2670 return errNone;
2673 int prSCImageKernel_Compile(struct VMGlobals *g, int numArgsPushed);
2674 int prSCImageKernel_Compile(struct VMGlobals *g, int numArgsPushed)
2676 PyrSlot* receiver = g->sp;
2677 return cachePyrKernel(g, slotRawObject(receiver));
2681 * This function is intended to apply a specific CIKernel to the image
2682 * Still experimental
2684 int prSCImageFilter_ApplyKernel(struct VMGlobals *g, int numArgsPushed);
2685 int prSCImageFilter_ApplyKernel(struct VMGlobals *g, int numArgsPushed)
2687 PyrSlot *receiver = g->sp - 3;
2688 PyrSlot *kernelSlot = g->sp - 2;
2689 PyrSlot *cropRegionSlot = g->sp - 1;
2690 PyrSlot *inPlace = g->sp;
2692 PyrSlot *argumentSlot;
2693 SCImage *newSCImage=nil;
2694 SCImage *scimage;
2695 PyrObject *kernelObject=nil;
2696 CIKernel *kernel=nil;
2698 int err;
2699 CGRect destExtent;
2700 CGRect cropRegion;
2703 err = errNone;
2704 scimage = (SCImage *)slotRawPtr(slotRawObject(receiver)->slots);
2705 if(!scimage){
2706 SetNil(receiver);
2707 return errNone;
2710 [scimage setAccelerated:YES];
2711 CIFilter* ciFilter = [[CIFilter new]autorelease];
2713 ///// Setting and caching the Kernel Object
2714 kernelObject = slotRawObject(kernelSlot);
2715 if(IsNil(kernelObject->slots+4)) {
2716 err = cachePyrKernel(g, kernelObject);
2717 if(err) return err;
2719 argumentSlot = kernelObject->slots+1;
2720 kernel = (CIKernel*)(slotRawPtr(&kernelObject->slots[4]));
2721 if(!kernel)
2722 return errFailed;
2724 // Get the extent of the image
2725 CGRect extent = [[scimage ciimage]extent];
2727 #if SCIMAGE_FILTER_DEBUG
2728 NSLog(@"SCImageFilter: CIKernel created Successfully !");
2729 #endif
2730 // Setting the Argument Array + PyrObject->Cocoa conversion
2731 // from the docs:
2733 You can pass the following types to a kernel routine:
2734 sampler:
2735 Requires a CISampler object when applied. // directly added in SCApp
2736 __table:
2737 A qualifier for a sampler type.
2738 float, vec2, vec3, vec4:
2739 Requires an NSNumber or CIVector.
2740 __color:
2741 A color that will be matched to the CIContext working color space when passed into the program.
2742 It requires a CIColor object when applied. To the kernel program it appears to be a vec4 type in premultiplied RGBA format.
2745 NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:16];
2746 if(!arguments) {
2747 post("Error: memory error ? allocation failed for NSMutableArray. applying Kernel aborted !");
2748 return errFailed;
2751 #if SCIMAGE_FILTER_DEBUG
2752 NSLog(@"SCImageFilter: CIKernel setting Arguments !");
2753 #endif
2754 // set all the arguments
2755 if( !IsNil(argumentSlot) ) { // iterate through all the arguments
2756 PyrObject * pyrArray = slotRawObject(argumentSlot);
2757 int size = pyrArray->size;
2758 for(int i=0; i < size; ++i) {
2759 id object = nsciObjectFromSlot(pyrArray->slots+i, NULL, NULL);
2760 if(object) {
2761 [arguments addObject:object];
2763 else
2764 return errFailed; // Kernels require no bad arguments
2768 // Create the Option Dictionary that contains additional info about final rendering
2769 // kCIApplyOptionDefinition : DOD of image
2770 // kCIApplyOptionExtent : size of the resulting image
2771 NSMutableDictionary * dictionary = [[[NSDictionary dictionary] mutableCopy] autorelease];
2773 [dictionary
2774 setValue:[[scimage ciimage] definition] // better to pass directly the filterShape
2775 forKey:kCIApplyOptionDefinition
2778 // Set the Extent of the result image if one is specified
2780 if(!IsNil(maxBounds)) {
2781 CGRect maxRect;
2782 err = slotGetNSRect(maxBounds, (NSRect*)&maxRect);
2783 if(err) goto Bail;
2784 [dictionary setValue:
2785 [NSArray arrayWithObjects:
2786 [NSNumber numberWithFloat:maxRect.origin.x], // x
2787 [NSNumber numberWithFloat:maxRect.origin.y], // y
2788 [NSNumber numberWithFloat:maxRect.size.width], // w
2789 [NSNumber numberWithFloat:maxRect.size.height] // h
2790 ,nil
2792 forKey:kCIApplyOptionExtent
2797 #if SCIMAGE_FILTER_DEBUG
2798 NSLog(@"SCImageFilter: CIKernel created Options successfully !");
2799 #endif
2801 CIImage *ciimage;
2802 @try {
2803 ciimage = [ciFilter apply:kernel arguments:arguments options:dictionary];
2805 // if the rect is not infinite :
2806 // 1 - crop the image if a cropRegion is specified
2807 // 2 - translate the image to align origin of extent with coordindate origin of image (0,0)
2809 if(IsNil(cropRegionSlot)) {
2810 cropRegion = OutsetRect(extent, floorf((SCCIIMAGE_MAX_WIDTH - extent.size.width) * 0.5), floorf((SCCIIMAGE_MAX_HEIGHT - extent.size.height) * 0.5));
2811 } else {
2812 err = slotGetNSRect(cropRegionSlot, (NSRect*)&cropRegion);
2813 if(err) return err;
2816 ciimage = CropAndCenterCIImage(ciimage, extent, &cropRegion);
2818 destExtent = [ciimage extent];
2820 #if SCIMAGE_FILTER_DEBUG
2821 NSLog(@"SCImageFilter: applied Kernel Successfully !");
2822 #endif
2824 // Setting the PyrSlot
2825 PyrObject* receiverObj;
2826 if(IsTrue(inPlace))
2828 if( CGSizeEqualToSize(extent.size, destExtent.size) ) {
2829 #if SCIMAGE_MAC_OS_10_5
2830 [[scimage accumulator] setImage:ciimage];
2831 #else
2832 [scimage->_ciimage release];
2833 scimage->_ciimage = [ciimage retain];
2834 #endif
2835 [scimage setShouldSyncBitmap:YES]; // invalidates bitmap
2836 return errNone;
2837 }else{
2838 NSLog(@"CIKernel different size returned");
2839 receiverObj = slotRawObject(receiver);
2842 else
2844 receiverObj = newPyrSCImage(g);
2847 #if SCIMAGE_FILTER_DEBUG
2848 NSLog(@"SCImageFilter: Creating New SCImage Class");
2849 #endif
2851 newSCImage = [[SCImage alloc]initWithCIImage:ciimage extent:destExtent format:[scimage format]];
2853 PyrSlot *slots = receiverObj->slots;
2854 [(id)slotRawPtr(slots) release];
2856 SetPtr(slots + 0, newSCImage);
2857 SetFloat(slots + 1, destExtent.size.width);
2858 SetFloat(slots + 2, destExtent.size.height);
2860 if(receiverObj != slotRawObject(receiver)) {
2861 SetObject(receiver, receiverObj);
2864 @catch ( NSException *e )
2866 //if([[e name]isEqualToString: @"CIKernelMissingArgument"])
2867 post("Error: %s\n", [[NSString stringWithFormat:@"%@ %@", @"SCImageKernel", e]UTF8String]);
2868 return errFailed;
2871 return errNone;
2874 int prSCImageFilter_GetAttributeMinMax(struct VMGlobals *g, int numArgsPushed)
2876 PyrSlot *receiver = g->sp - 2;
2877 PyrSlot *filterAttributeSlot= g->sp - 1;
2878 PyrSlot *returnedArraySlot = g->sp;
2879 CIFilter *ciFilter;
2881 ciFilter = [CIFilter filterWithName: nsStringFromSlot(slotRawObject(receiver)->slots+0)]; // filter name
2882 if(!ciFilter) return errFailed;
2884 NSDictionary* attrDict = [ciFilter attributes];
2885 NSString *key = nsStringFromSlot(filterAttributeSlot);
2886 id attribute;
2888 #if SCIMAGE_FILTER_DEBUG
2889 NSLog(@"Checking for CIFilter MinMax of attribute: %@", key);
2890 #endif
2892 if( !key ) {
2893 SetNil(receiver);
2894 return errNone;
2896 // goto BailErr;
2898 const char* cStr = [key cStringUsingEncoding:NSASCIIStringEncoding];
2899 attribute = [attrDict objectForKey: [NSString stringWithFormat:@"input%c%s", toupper(*cStr), cStr+1]];
2900 if( !attribute ) {
2901 post("SCImageFilter : no attributes named %s found !\n", [key UTF8String]);
2902 SetNil(receiver);
2903 return errNone;
2904 // goto BailErr;
2907 PyrObject* pyrArray = slotRawObject(returnedArraySlot); // should be an array of size 3
2908 NSString* attrType = [attribute objectForKey:kCIAttributeType];
2910 id minmax[3];
2911 minmax[0] = [attribute objectForKey: kCIAttributeSliderMin] ? [attribute objectForKey: kCIAttributeSliderMin] : [attribute objectForKey: kCIAttributeMin];
2912 minmax[1] = [attribute objectForKey: kCIAttributeSliderMax] ? [attribute objectForKey: kCIAttributeSliderMax] : [attribute objectForKey: kCIAttributeMax];
2913 minmax[2] = [attribute objectForKey: kCIAttributeDefault];
2915 if(!minmax[0] && !minmax[1] && !minmax[2]){ // if no slider min or slider max or default - set to nil directly
2916 //goto BailErr;
2917 SetNil(receiver);
2918 return errNone;
2921 if(attrType == kCIAttributeTypeBoolean)
2923 SetFalse(pyrArray->slots+0);
2924 SetTrue(pyrArray->slots+1);
2925 SetBool(pyrArray->slots+2, ([ minmax[2] floatValue ] > 0.f));
2927 else if(
2928 attrType == kCIAttributeTypeAngle || attrType == kCIAttributeTypeDistance ||
2929 attrType == kCIAttributeTypeScalar || attrType == kCIAttributeTypeTime
2932 if(minmax[0])
2933 SetFloat(pyrArray->slots+0, [minmax[0] doubleValue]);
2934 else
2935 SetNil(pyrArray->slots+0);
2937 if(minmax[1])
2938 SetFloat(pyrArray->slots+1, [minmax[1] doubleValue]);
2939 else
2940 SetNil(pyrArray->slots+1);
2942 if(minmax[2])
2943 SetFloat(pyrArray->slots+2, [minmax[2] doubleValue]);
2944 else
2945 SetNil(pyrArray->slots+1);
2947 else if( attrType == kCIAttributeTypePosition || attrType == kCIAttributeTypeOffset )
2949 // 2-element vector
2950 PyrObject* vector[3];
2951 for(int i=0; i < 3; ++i) {
2952 if( ! minmax[i] ) {
2953 SetNil(pyrArray->slots+i); continue;
2955 vector[i] = newPyrArray(g->gc, sizeof(double) * 2, 0, true);
2956 SetFloat( vector[i]->slots+0, [ (CIVector*)minmax[i] X ]);
2957 SetFloat( vector[i]->slots+1, [ (CIVector*)minmax[i] Y ]);
2958 vector[i]->size = 2;
2959 SetObject(pyrArray->slots+i, vector[i]);
2960 g->gc->GCWrite(pyrArray, vector[i]);
2963 else if( attrType == kCIAttributeTypePosition3 )
2965 // 3-element vector
2966 PyrObject* vector[3];
2967 for(int i=0; i < 3; ++i) {
2968 if( ! minmax[i] ) {
2969 SetNil(pyrArray->slots+i); continue;
2971 vector[i] = newPyrArray(g->gc, sizeof(double) * 3, 0, true);
2972 SetFloat( vector[i]->slots+0, [ (CIVector*)minmax[i] X ]);
2973 SetFloat( vector[i]->slots+1, [ (CIVector*)minmax[i] Y ]);
2974 SetFloat( vector[i]->slots+2, [ (CIVector*)minmax[i] Z ]);
2975 vector[i]->size = 3;
2976 SetObject(pyrArray->slots+i, vector[i]);
2977 g->gc->GCWrite(pyrArray, vector[i]);
2980 else if( attrType == kCIAttributeTypeRectangle )
2982 // 4-element vector
2983 PyrObject* vector[3];
2984 for(int i=0; i < 3; ++i) {
2985 if( ! minmax[i] ) {
2986 SetNil(pyrArray->slots+i); continue;
2988 vector[i] = newPyrArray(g->gc, sizeof(double) * 4, 0, true);
2989 SetFloat( vector[i]->slots+0, [ (CIVector*)minmax[i] X ]);
2990 SetFloat( vector[i]->slots+1, [ (CIVector*)minmax[i] Y ]);
2991 SetFloat( vector[i]->slots+2, [ (CIVector*)minmax[i] Z ]);
2992 SetFloat( vector[i]->slots+3, [ (CIVector*)minmax[i] W ]);
2993 vector[i]->size = 4;
2994 SetObject(pyrArray->slots+i, vector[i]);
2995 g->gc->GCWrite(pyrArray, vector[i]);
2998 else if( attrType == kCIAttributeTypeOpaqueColor )
3000 // 4-element vector
3001 PyrObject* vector[3];
3002 for(int i=0; i < 3; ++i) {
3003 vector[i] = ::instantiateObject(g->gc, s_color->u.classobj, 0, false, true);
3004 SetFloat( vector[i]->slots+0, (double)[ (CIColor*)minmax[i] red ]);
3005 SetFloat( vector[i]->slots+1, (double)[ (CIColor*)minmax[i] green ]);
3006 SetFloat( vector[i]->slots+2, (double)[ (CIColor*)minmax[i] blue ]);
3007 SetFloat( vector[i]->slots+3, (double)[ (CIColor*)minmax[i] alpha ]);
3008 SetObject(pyrArray->slots+i, vector[i]);
3009 g->gc->GCWrite(pyrArray, vector[i]);
3012 else {
3013 SetNil(receiver);
3014 return errNone;
3016 // goto BailErr;
3018 SetObject(receiver, pyrArray);
3019 return errNone;
3021 // BailErr:
3022 // SetNil(receiver);
3023 // return errNone;
3026 int prSCImageFilter_Attributes(struct VMGlobals *g, int numArgsPushed);
3027 int prSCImageFilter_Attributes(struct VMGlobals *g, int numArgsPushed)
3029 PyrSlot *receiver = g->sp - 1;
3030 PyrSlot *filterNameSlot = g->sp;
3031 CIFilter *filter;
3033 filter = [CIFilter filterWithName: nsStringFromSlot(filterNameSlot)];
3034 if(!filter)
3035 return errFailed;
3037 NSDictionary* attr = [filter attributes];
3038 NSArray *inputKeys = [filter inputKeys];
3040 NSEnumerator* e = [inputKeys objectEnumerator];
3041 NSString *key;
3043 int maxsize, i=0;
3044 maxsize = [inputKeys count] * 2;
3045 if([inputKeys containsObject:@"inputImage"]) {
3046 maxsize -= 2; // remove the inputImage
3048 PyrSymbolArray* returnObject = newPyrSymbolArray(g->gc, maxsize, 0, true);
3049 returnObject->size = maxsize;
3051 const char* cStr;
3052 while( (key = [e nextObject]) )
3054 if(i >= maxsize) return errNone; // in case
3055 if(![key isEqualToString:@"inputImage"]) { // we do not need it
3056 cStr = [[key substringFromIndex:5] cStringUsingEncoding:NSASCIIStringEncoding]; // remove the 'input'
3057 returnObject->symbols[i++] = getsym([ [NSString stringWithFormat: @"%c%s", tolower(*cStr), (cStr + 1)] UTF8String]);
3058 returnObject->symbols[i++] = getsym([ [ [ attr objectForKey: key] objectForKey: @"CIAttributeClass" ] UTF8String ]);
3062 SetObject(receiver, returnObject);
3064 return errNone;
3067 int prSCImageFilter_NamesInCategory(struct VMGlobals *g, int numArgsPushed);
3068 int prSCImageFilter_NamesInCategory(struct VMGlobals *g, int numArgsPushed)
3070 PyrSlot *receiver, *category, *returnArray;
3071 NSString *type;
3072 NSArray *array;
3074 returnArray = g->sp;
3075 category = g->sp- 1;
3076 receiver = g->sp - 2;
3078 //PyrObject *returnObject = slotRawObject(returnArray);
3079 //int maxsize = MAXINDEXSIZE(returnObject);
3081 if (!IsSym(category)) return errWrongType;
3083 type = nsStringFromSlot(category);
3084 if(!type) return errFailed;
3086 array = [CIFilter filterNamesInCategory:type];
3087 if(!array) return errFailed;
3089 int maxsize = [array count];
3090 PyrSymbolArray* returnObject = newPyrSymbolArray(g->gc, maxsize, 0, true);
3091 returnObject->size = maxsize;
3093 int i=0;
3094 NSEnumerator *e = [array objectEnumerator];
3097 while( (type = [e nextObject]) )
3099 if(i >= maxsize) break; // in case
3100 returnObject->symbols[i] = getsym([type UTF8String]);
3101 ++i;
3104 SetObject(receiver, returnObject);
3106 return errNone;
3109 #pragma mark - InitSCImagePrims
3111 void initSCImagePrimitives()
3113 int base, index;
3115 s_scimage = getsym("SCImage");
3116 s_scfilter = getsym("SCImageFilter");
3117 s_scimagekernel = getsym("SCImageKernel");
3119 #if SCIMAGE_DEBUG
3120 NSLog(@"---- SCIMAGE: Init Primitives ----");
3121 NSLog(@"---- SCIMAGE: Using Mac OS 10.%c Version ----", (SCIMAGE_MAC_OS_10_5 == 1) ? '5' : '4');
3122 #endif
3124 if(!gCIFilterPlugInsLoaded) {
3125 #if SCIMAGE_DEBUG
3126 NSLog(@"---- SCIMAGE: Loading CIFilter PlugIns ----");
3127 #endif
3128 [CIPlugIn loadAllPlugIns];
3129 gCIFilterPlugInsLoaded = YES;
3132 base = nextPrimitiveIndex();
3133 index = 0;
3136 definePrimitive(base, index++, "_SCImage_New", prSCImage_New, 4, 0);
3137 //definePrimitive(base, index++, "_SCImage_NewFromFile", prSCImage_NewFromFile, 2, 0); // use only URL
3138 definePrimitive(base, index++, "_SCImage_NewFromURL", prSCImage_NewFromURL, 2, 0);
3139 definePrimitive(base, index++, "_SCImage_WriteToFile", prSCImage_WriteToFile, 3, 0);
3140 definePrimitive(base, index++, "_SCImage_Free", prSCImage_Free, 1, 0);
3141 definePrimitive(base, index++, "_SCImage_setSize", prSCImage_setSize, 3, 0);
3142 definePrimitive(base, index++, "_SCImage_setScalesWhenResized", prSCImage_setScalesWhenResized, 2, 0);
3143 definePrimitive(base, index++, "_SCImage_scalesWhenResized", prSCImage_scalesWhenResized, 1, 0);
3144 definePrimitive(base, index++, "_SCImage_DrawAtPoint", prSCImage_DrawAtPoint, 5, 0);
3145 definePrimitive(base, index++, "_SCImage_DrawInRect", prSCImage_DrawInRect, 5, 0);
3146 definePrimitive(base, index++, "_SCImage_tileInRect", prSCImage_TileInRect, 5, 0);
3147 definePrimitive(base, index++, "_SCImage_lockFocus", prSCImage_lockFocus, 1, 0);
3148 definePrimitive(base, index++, "_SCImage_unlockFocus", prSCImage_unlockFocus, 1, 0);
3149 definePrimitive(base, index++, "_SCImage_recache", prSCImage_recache, 1, 0);
3150 definePrimitive(base, index++, "_SCImage_setPixelAt", prSCImage_setPixelAt, 4, 0);
3151 definePrimitive(base, index++, "_SCImage_getPixelAt", prSCImage_pixelAt, 3, 0);
3152 definePrimitive(base, index++, "_SCImage_setColorAt", prSCImage_setColorAt, 4, 0);
3153 definePrimitive(base, index++, "_SCImage_getColorAt", prSCImage_getColorAt, 3, 0);
3154 definePrimitive(base, index++, "_SCImage_setAccelerated", prSCImage_setAccelerated, 2, 0);
3155 definePrimitive(base, index++, "_SCImage_isAccelerated", prSCImage_isAccelerated, 1, 0);
3156 definePrimitive(base, index++, "_SCImage_loadPixels", prSCImage_loadPixels, 4, 0);
3157 definePrimitive(base, index++, "_SCImage_updatePixels", prSCImage_updatePixels, 3, 0);
3158 definePrimitive(base, index++, "_SCImage_updatePixelsInRect", prSCImage_updatePixelsInRect, 4, 0);
3159 definePrimitive(base, index++, "_SCImage_interpolation", prSCImage_interpolation, 1, 0);
3160 definePrimitive(base, index++, "_SCImage_setInterpolation", prSCImage_setInterpolation, 2, 0);
3161 definePrimitive(base, index++, "_SCImage_sync", prSCImage_sync, 1, 0);
3162 definePrimitive(base, index++, "_SCImage_fromWindowRect", prSCImage_imageFromSCWindowRect, 3, 0);
3164 // image Filter - experimental for now
3165 definePrimitive(base, index++, "_SCImageFilter_NamesInCategory", prSCImageFilter_NamesInCategory, 3, 0);
3166 definePrimitive(base, index++, "_SCImageFilter_Attributes", prSCImageFilter_Attributes, 2, 0);
3167 definePrimitive(base, index++, "_SCImageFilter_Apply", prSCImageFilter_Apply, 4, 0);
3168 definePrimitive(base, index++, "_SCImageFilter_ApplyMultiple", prSCImageFilter_ApplyMultiple, 5, 0);
3169 definePrimitive(base, index++, "_SCImageFilter_GetAttributeMinMax", prSCImageFilter_GetAttributeMinMax, 3, 0);
3170 definePrimitive(base, index++, "_SCImageFilter_ApplyKernel", prSCImageFilter_ApplyKernel, 4, 0);
3171 definePrimitive(base, index++, "_SCImageKernel_Compile", prSCImageKernel_Compile, 1, 0);
3175 #else
3177 void initSCImagePrimitives()
3179 // SCImage not supported for os version < 10.4...
3182 #endif