Vuo  2.3.2
VuoRunnerCocoa.mm
Go to the documentation of this file.
1 
10 #include "VuoRunnerCocoa.h"
12 
13 #include <OpenGL/CGLMacro.h>
14 #include "VuoMacOSSDKWorkaround.h"
15 #include <QuartzCore/CoreImage.h>
16 
17 #include <dispatch/dispatch.h>
18 
19 #include <zmq/zmq.h>
20 #include <vector>
21 #include <set>
22 #include "VuoRunner.hh"
23 #include "VuoCompiler.hh"
24 #include "VuoCompilerIssue.hh"
25 #include "VuoComposition.hh"
27 #include "VuoException.hh"
28 #include "VuoProtocol.hh"
29 #include "VuoHeap.h"
30 #include "VuoGlContext.h"
31 #include "VuoGlPool.h"
32 
33 extern "C" {
34 #include "VuoInteger.h"
35 #include "VuoImageGet.h"
36 }
37 
39 
43 @interface VuoRunnerCocoa ()
44 @property dispatch_queue_t runnerQueue;
45 @property VuoRunner *runner;
46 @property (retain) NSURL *compositionURL;
47 @property (retain) NSString *compositionString;
48 @property (retain) NSString *compositionSourcePath;
49 @property (retain) NSString *compositionProcessName;
50 @property (retain) NSString *compositionName;
51 @property (retain) NSString *compositionDescription;
52 @property (retain) NSString *compositionCopyright;
53 @property VuoProtocol *protocol;
54 @property set<VuoRunner::Port *> *changedPorts;
55 @end
56 
57 @implementation VuoRunnerCocoa
58 @synthesize runnerQueue;
59 @synthesize runner;
60 @synthesize compositionURL;
61 @synthesize compositionString;
62 @synthesize compositionSourcePath;
63 @synthesize compositionProcessName;
64 @synthesize compositionName;
65 @synthesize compositionDescription;
66 @synthesize compositionCopyright;
67 @synthesize protocol;
68 @synthesize changedPorts;
69 
80 + (void)setGlobalRootContext:(CGLContextObj)context
81 {
83 }
84 
93 + (void)prepareForFastBuild
94 {
95  @synchronized(self)
96  {
98  {
99  VuoCompiler c;
102  }
103  }
104 }
105 
109 + (BOOL)isComposition:(NSURL *)compositionURL compliantWithProtocol:(VuoProtocol *)protocol
110 {
111  VuoRunnerCocoa *runner = [[VuoRunnerCocoa alloc] initWithComposition:compositionURL protocol:protocol];
112  if (!runner)
113  return NO;
114 
115  [runner release];
116  return YES;
117 }
118 
122 + (BOOL)isCompositionString:(NSString *)compositionString compliantWithProtocol:(VuoProtocol *)protocol
123 {
124  VuoRunnerCocoa *runner = [[VuoRunnerCocoa alloc] initWithCompositionString:compositionString name:@"" sourcePath:nil protocol:protocol];
125  if (!runner)
126  return NO;
127 
128  [runner release];
129  return YES;
130 }
131 
135 - (id)initWithComposition:(NSURL *)theCompositionURL protocol:(VuoProtocol *)theProtocol
136 {
137  NSError *error;
138  NSString *s = [NSString stringWithContentsOfURL:theCompositionURL encoding:NSUTF8StringEncoding error:&error];
139  if (!s)
140  {
141  NSLog(@"[VuoRunnerCocoa initWithComposition:protocol:] Error reading composition: %@", error);
142  [self release];
143  return nil;
144  }
145 
146  self = [self initWithCompositionString:s name:[theCompositionURL lastPathComponent] sourcePath:nil protocol:theProtocol];
147  if (!self)
148  return nil;
149 
150  self.compositionURL = theCompositionURL;
151 
152  return self;
153 }
154 
158 - (id)initWithCompositionString:(NSString *)theCompositionString name:(NSString *)processName sourcePath:(NSString *)sourcePath protocol:(VuoProtocol *)theProtocol
159 {
160  if (!(self = [super init]))
161  return nil;
162 
163  self.protocol = theProtocol;
164 
165  self.runnerQueue = dispatch_queue_create("org.vuo.runner.cocoa", NULL);
166  self.runner = NULL;
167 
168  self.compositionString = theCompositionString;
169  const char *compositionCString = [self.compositionString UTF8String];
170  if (!theProtocol->isCompositionCompliant(compositionCString))
171  {
172  [self release];
173  return nil;
174  }
175 
176  self.compositionProcessName = processName;
177  self.compositionSourcePath = sourcePath;
178 
179  VuoCompositionMetadata metadata(compositionCString);
180  self.compositionName = [NSString stringWithUTF8String:metadata.getName().c_str()];
181  self.compositionDescription = [NSString stringWithUTF8String:metadata.getDescription().c_str()];
182  self.compositionCopyright = [NSString stringWithUTF8String:metadata.getCopyright().c_str()];
183 
184  self.changedPorts = new set<VuoRunner::Port *>();
185 
186  return self;
187 }
188 
192 - (void)dealloc
193 {
194  dispatch_sync(self.runnerQueue, ^{
195  if (self.runner)
196  {
197  self.runner->stop();
198  delete self.runner;
199  }
200  });
201  dispatch_release(self.runnerQueue);
202  [compositionURL release];
203  [compositionString release];
204  [compositionSourcePath release];
205  [compositionName release];
206  [compositionDescription release];
207  [compositionCopyright release];
208  delete self.changedPorts;
209  [super dealloc];
210 }
211 
212 - (void)compileAndRun
213 {
215 
216  dispatch_sync(self.runnerQueue, ^{
217  VuoCompilerIssues issues;
218  if (self.compositionURL)
220  [[self.compositionURL path] UTF8String],
221  &issues);
222  else
224  [self.compositionString UTF8String],
225  [self.compositionProcessName UTF8String],
226  [self.compositionSourcePath UTF8String],
227  &issues);
228 
229  if (!issues.isEmpty())
230  VUserLog("%s", issues.getLongDescription(false).c_str());
231 
232  if (self.runner)
233  {
234  self.runner->start();
235  self.runner->subscribeToEventTelemetry("");
236 
237  try
238  {
239  vector<VuoRunner::Port *> allInputPorts = self.runner->getPublishedInputPorts();
240  std::copy_if(allInputPorts.begin(), allInputPorts.end(), std::inserter(*self.changedPorts, self.changedPorts->begin()),
241  [] (VuoRunner::Port *port) { return ! port->getType().empty(); });
242  }
243  catch (VuoException &e)
244  {
245  VUserLog("Error: %s", e.what());
246  }
247  }
248  });
249 }
250 
254 - (BOOL)isStopped
255 {
256  __block BOOL isStopped = YES;
257  dispatch_sync(self.runnerQueue, ^{
258  if (self.runner && !self.runner->isStopped())
259  isStopped = NO;
260  });
261  return isStopped;
262 }
263 
267 - (NSString *)description
268 {
269  return [NSString stringWithFormat:@"VuoRunnerCocoa(\"%@\")", compositionName];
270 }
271 
276 - (NSArray *)inputPorts
277 {
278  if ([self isStopped])
279  return nil;
280 
281  NSMutableArray *portsArray = [[NSMutableArray new] autorelease];
282  vector<VuoRunner::Port *> ports = self.runner->getPublishedInputPorts();
283  for (vector<VuoRunner::Port *>::iterator it = ports.begin(); it != ports.end(); ++it)
284  if (self.protocol && !self.protocol->hasInputPort((*it)->getName()))
285  [portsArray addObject:[NSString stringWithUTF8String:(*it)->getName().c_str()]];
286  return portsArray;
287 }
288 
293 - (NSArray *)outputPorts
294 {
295  if ([self isStopped])
296  return nil;
297 
298  NSMutableArray *portsArray = [[NSMutableArray new] autorelease];
299  vector<VuoRunner::Port *> ports = self.runner->getPublishedOutputPorts();
300  for (vector<VuoRunner::Port *>::iterator it = ports.begin(); it != ports.end(); ++it)
301  if (self.protocol && !self.protocol->hasOutputPort((*it)->getName()))
302  [portsArray addObject:[NSString stringWithUTF8String:(*it)->getName().c_str()]];
303  return portsArray;
304 }
305 
335 - (NSDictionary *)detailsForPort:(NSString *)portName
336 {
337  const char *portNameCString = [portName UTF8String];
338  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName(portNameCString);
339  if (!port)
340  {
341  port = self.runner->getPublishedOutputPortWithName(portNameCString);
342  if (!port)
343  return nil;
344  }
345 
346  NSMutableDictionary *details = [[NSMutableDictionary new] autorelease];
347 
348  details[@"title"] = [NSString stringWithUTF8String:port->getName().c_str()];
349 
350  details[@"type"] = [NSString stringWithUTF8String:port->getType().c_str()];
351 
352  json_object *js = port->getDetails();
353  if (js)
354  {
355  json_object *o = NULL;
356 
357  if (json_object_object_get_ex(js, "default", &o))
358  {
359  id cocoaObject = [VuoRunnerCocoa cocoaObjectWithVuoValue:o ofType:port->getType()];
360  if (cocoaObject)
361  details[@"default"] = cocoaObject;
362  }
363 
364  if (json_object_object_get_ex(js, "suggestedMin", &o))
365  {
366  id cocoaObject = [VuoRunnerCocoa cocoaObjectWithVuoValue:o ofType:port->getType()];
367  if (cocoaObject)
368  details[@"suggestedMin"] = cocoaObject;
369  }
370 
371  if (json_object_object_get_ex(js, "suggestedMax", &o))
372  {
373  id cocoaObject = [VuoRunnerCocoa cocoaObjectWithVuoValue:o ofType:port->getType()];
374  if (cocoaObject)
375  details[@"suggestedMax"] = cocoaObject;
376  }
377 
378  if (json_object_object_get_ex(js, "suggestedStep", &o))
379  {
380  id cocoaObject = [VuoRunnerCocoa cocoaObjectWithVuoValue:o ofType:port->getType()];
381  if (cocoaObject)
382  details[@"suggestedStep"] = cocoaObject;
383  }
384 
385  if (json_object_object_get_ex(js, "menuItems", &o))
386  {
387  NSMutableArray *menuItems = [NSMutableArray new];
388 
389  int itemCount = json_object_array_length(o);
390  for (int i = 0; i < itemCount; ++i)
391  {
392  json_object *item = json_object_array_get_idx(o, i);
393  id menuItem = nil;
394 
395  if (json_object_is_type(item, json_type_object))
396  {
397  json_object *value;
398  json_object_object_get_ex(item, "value", &value);
399  json_object *name;
400  json_object_object_get_ex(item, "name", &name);
401 
402  id valueNS = nil;
403  if (json_object_is_type(value, json_type_string))
404  valueNS = [NSString stringWithUTF8String:json_object_get_string(value)];
405  else if (json_object_is_type(value, json_type_int))
406  valueNS = [NSNumber numberWithLong:json_object_get_int64(value)];
407 
408  const char *summary = json_object_get_string(name);
409  menuItem = @{
410  @"value": valueNS,
411  @"name": [NSString stringWithUTF8String:summary],
412  };
413  }
414  else if (json_object_is_type(item, json_type_string))
415  menuItem = [NSString stringWithUTF8String:json_object_get_string(item)];
416 
417  if (menuItem)
418  [menuItems addObject:menuItem];
419  }
420 
421  details[@"menuItems"] = menuItems;
422  [menuItems release];
423  }
424  }
425 
426  return details;
427 }
428 
433 {
434  if ([self isStopped])
435  return nil;
436 
437  NSMutableDictionary *properties = [[NSMutableDictionary new] autorelease];
438 
439  vector<VuoRunner::Port *> ports = self.runner->getPublishedInputPorts();
440  for (vector<VuoRunner::Port *>::iterator it = ports.begin(); it != ports.end(); ++it)
441  if (self.protocol && !self.protocol->hasInputPort((*it)->getName()))
442  {
443  json_object *portValue = self.runner->getPublishedInputPortValue(*it);
444  const char *valueAsString = json_object_to_json_string_ext(portValue, JSON_C_TO_STRING_PLAIN);
445  properties[[NSString stringWithUTF8String:(*it)->getName().c_str()]] = [NSString stringWithUTF8String:valueAsString];
446  }
447 
448  return properties;
449 }
450 
457 - (BOOL)setInputValuesWithPropertyList:(id)propertyList
458 {
459  if ([self isStopped])
460  return NO;
461 
462  __block BOOL ok = YES;
463  __block map<VuoRunner::Port *, json_object *> m;
464  [(NSDictionary *)propertyList enumerateKeysAndObjectsUsingBlock:^(NSString *portName, NSString *value, BOOL *stop) {
465  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName([portName UTF8String]);
466  if (!port)
467  {
468  ok = NO;
469  return;
470  }
471 
472  json_object *valueJson = json_tokener_parse([value UTF8String]);
473  if (!valueJson)
474  {
475  ok = NO;
476  return;
477  }
478 
479  m[port] = valueJson;
480  }];
481 
482  runner->setPublishedInputPortValues(m);
483 
484  for (auto &kv : m)
485  {
486  self.changedPorts->insert(kv.first);
487  json_object_put(kv.second);
488  }
489 
490  return ok;
491 }
492 
508 - (BOOL)setInputValues:(NSDictionary *)namesAndValues
509 {
510  if ([self isStopped])
511  return NO;
512 
513  BOOL ok = YES;
514  map<VuoRunner::Port *, json_object *> m;
515  for (NSString *portName in namesAndValues)
516  {
517  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName([portName UTF8String]);
518  if (!port)
519  {
520  ok = NO;
521  continue;
522  }
523 
524  id value = namesAndValues[portName];
526  if (!valueJson)
527  {
528  ok = NO;
529  continue;
530  }
531 
532  m[port] = valueJson;
533  }
534 
535  self.runner->setPublishedInputPortValues(m);
536 
537  for (auto &kv : m)
538  {
539  self.changedPorts->insert(kv.first);
540  json_object_put(kv.second);
541  }
542 
543  return ok;
544 }
545 
553 - (BOOL)setInputJSON:(NSDictionary *)namesAndJSON
554 {
555  if ([self isStopped])
556  return NO;
557 
558  BOOL ok = YES;
559  map<VuoRunner::Port *, json_object *> m;
560  for (NSString *portName in namesAndJSON)
561  {
562  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName([portName UTF8String]);
563  if (!port)
564  {
565  ok = NO;
566  continue;
567  }
568 
569  id nsValue = namesAndJSON[portName];
570  json_object *value = (json_object *)[nsValue pointerValue];
571 
572  // If we're feeding a string to a VuoImage port, assume it's an image URL, and try to load it.
573  if (port->getType() == "VuoImage")
574  {
575  if (json_object_get_type(value) == json_type_string)
576  {
577  char *s = strdup(json_object_get_string(value));
578  VuoImage image = VuoImage_get(s);
579  if (!image)
580  {
581  NSLog(@"Error: Couldn't load image '%s'.\n", s);
582  free(s);
583  ok = NO;
584  continue;
585  }
586  free(s);
587 
588  m[port] = VuoImage_getInterprocessJson(image);
589  }
590  }
591  else
592  m[port] = value;
593  }
594 
595  self.runner->setPublishedInputPortValues(m);
596 
597  for (auto &kv : m)
598  self.changedPorts->insert(kv.first);
599 
600  return ok;
601 }
602 
620 - (id)valueForOutputPort:(NSString *)portName
621 {
622  if ([self isStopped])
623  return nil;
624 
625  VuoRunner::Port *port = self.runner->getPublishedOutputPortWithName([portName UTF8String]);
626  if (!port)
627  return nil;
628 
629  json_object *valueJson = self.runner->getPublishedOutputPortValue(port);
630  return [VuoRunnerCocoa cocoaObjectWithVuoValue:valueJson ofType:port->getType()];
631 }
632 
642 - (GLuint)glTextureWithTarget:(GLuint)target forOutputPort:(NSString *)portName outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh
643 {
644  if ([self isStopped])
645  return 0;
646 
647  VuoRunner::Port *port = self.runner->getPublishedOutputPortWithName([portName UTF8String]);
648  if (!port)
649  return 0;
650 
651  json_object *valueJson = self.runner->getPublishedOutputPortValue(port);
652  VuoImage vuoImage = VuoImage_makeFromJson(valueJson);
653  if (!vuoImage)
654  return 0;
655  json_object_put(valueJson);
656 
657  if (target == GL_TEXTURE_RECTANGLE_ARB)
658  {
659  VuoRetain(vuoImage);
660  VuoImage newVuoImage = VuoImage_makeGlTextureRectangleCopy(vuoImage);
661  VuoRelease(vuoImage);
662  vuoImage = newVuoImage;
663  }
664 
665  if (outputPixelsWide)
666  *outputPixelsWide = vuoImage->pixelsWide;
667  if (outputPixelsHigh)
668  *outputPixelsHigh = vuoImage->pixelsHigh;
669 
670  VuoGlTexture_disown(vuoImage->glTextureName);
671 
672  return vuoImage->glTextureName;
673 }
674 
694 - (GLuint)glTextureFromProvider:(GLuint (^)(NSUInteger pixelsWide, NSUInteger pixelsHigh))provider forOutputPort:(NSString *)portName outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh ioSurface:(IOSurfaceRef *)outputIOSurface
695 {
696  if ([self isStopped])
697  return 0;
698 
699  VuoRunner::Port *port = self.runner->getPublishedOutputPortWithName([portName UTF8String]);
700  if (!port)
701  return 0;
702 
703  json_object *valueJson = self.runner->getPublishedOutputPortValue(port);
704 
705  GLuint (^provider2)(unsigned int, unsigned int) = ^GLuint(unsigned int pixelsWide, unsigned int pixelsHigh){
706  return provider(pixelsWide, pixelsHigh);
707  };
708  unsigned int outputPixelsWide2, outputPixelsHigh2;
709  GLuint outputTexture = VuoImage_resolveInterprocessJsonUsingTextureProvider(valueJson, provider2, &outputPixelsWide2, &outputPixelsHigh2, outputIOSurface);
710  *outputPixelsWide = outputPixelsWide2;
711  *outputPixelsHigh = outputPixelsHigh2;
712  return outputTexture;
713 }
714 
715 @end
716 
717 
718 @implementation VuoImageFilter
722 + (BOOL)canOpenComposition:(NSURL *)compositionURL
723 {
724  return [self isComposition:compositionURL compliantWithProtocol:VuoProtocol::getProtocol(VuoProtocol::imageFilter)];
725 }
726 
730 + (BOOL)canOpenCompositionString:(NSString *)compositionString
731 {
732  return [self isCompositionString:compositionString compliantWithProtocol:VuoProtocol::getProtocol(VuoProtocol::imageFilter)];
733 }
734 
741 - (id)initWithComposition:(NSURL *)aCompositionURL
742 {
743  if (!(self = [super initWithComposition:aCompositionURL protocol:VuoProtocol::getProtocol(VuoProtocol::imageFilter)]))
744  return nil;
745 
746  [self compileAndRun];
747  if (! self.runner)
748  return nil;
749 
750  return self;
751 }
752 
760 - (id)initWithCompositionString:(NSString *)aCompositionString name:(NSString *)name sourcePath:(NSString *)sourcePath
761 {
762  if (!(self = [super initWithCompositionString:aCompositionString name:name sourcePath:sourcePath protocol:VuoProtocol::getProtocol(VuoProtocol::imageFilter)]))
763  return nil;
764 
765  [self compileAndRun];
766  if (! self.runner)
767  return nil;
768 
769  return self;
770 }
771 
775 static void VuoRunnerCocoa_doNothingCallback(VuoImage imageToFree)
776 {
777 }
778 
782 - (BOOL)executeWithGLTexture:(GLuint)textureName target:(GLuint)target pixelsWide:(unsigned long)pixelsWide pixelsHigh:(unsigned long)pixelsHigh atTime:(NSTimeInterval)time
783 {
784  if ([self isStopped])
785  return NO;
786 
787  {
788  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName("image");
789  VuoImage image = VuoImage_makeClientOwned(textureName, GL_RGBA, pixelsWide, pixelsHigh, VuoRunnerCocoa_doNothingCallback, NULL);
790  image->glTextureTarget = target;
791  VuoRetain(image);
792  json_object *valueJson = VuoImage_getInterprocessJson(image);
793  VuoRelease(image);
794  map<VuoRunner::Port *, json_object *> m;
795  m[port] = valueJson;
796  self.runner->setPublishedInputPortValues(m);
797  json_object_put(valueJson);
798 
799  self.changedPorts->insert(port);
800  }
801 
802  return [self executeAtTime:time];
803 }
804 
808 - (BOOL)executeAtTime:(NSTimeInterval)time
809 {
810  if ([self isStopped])
811  return NO;
812 
813  VuoRunner::Port *timePort = self.runner->getPublishedInputPortWithName("time");
814 
815  {
816  json_object *valueJson = VuoReal_getJson(time);
817  map<VuoRunner::Port *, json_object *> m;
818  m[timePort] = valueJson;
819  self.runner->setPublishedInputPortValues(m);
820  json_object_put(valueJson);
821 
822  self.changedPorts->insert(timePort);
823  }
824 
825  self.runner->firePublishedInputPortEvent(*self.changedPorts);
826  self.changedPorts->clear();
827 
828  self.runner->waitForFiredPublishedInputPortEvent();
829  return YES;
830 }
831 
845 - (NSImage *)filterNSImage:(NSImage *)image atTime:(NSTimeInterval)time
846 {
847  [self setInputValues:@{@"image":image}];
848 
849  if (![self executeAtTime:time])
850  return nil;
851 
852  return [self valueForOutputPort:@"outputImage"];
853 }
854 
869 - (GLuint)filterGLTexture:(GLuint)textureName target:(GLuint)target pixelsWide:(NSUInteger)pixelsWide pixelsHigh:(NSUInteger)pixelsHigh atTime:(NSTimeInterval)time outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh
870 {
871  if (![self executeWithGLTexture:textureName target:target pixelsWide:pixelsWide pixelsHigh:pixelsHigh atTime:time])
872  return 0;
873  return [self glTextureWithTarget:target forOutputPort:@"outputImage" outputPixelsWide:outputPixelsWide pixelsHigh:outputPixelsHigh];
874 }
875 
905 - (GLuint)filterGLTexture:(GLuint)textureName target:(GLuint)target pixelsWide:(NSUInteger)pixelsWide pixelsHigh:(NSUInteger)pixelsHigh atTime:(NSTimeInterval)time withTextureProvider:(GLuint (^)(NSUInteger pixelsWide, NSUInteger pixelsHigh))provider outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh ioSurface:(IOSurfaceRef *)outputIOSurface
906 {
907  if (![self executeWithGLTexture:textureName target:target pixelsWide:pixelsWide pixelsHigh:pixelsHigh atTime:time])
908  return 0;
909 
910  return [self glTextureFromProvider:provider forOutputPort:@"outputImage" outputPixelsWide:outputPixelsWide pixelsHigh:outputPixelsHigh ioSurface:outputIOSurface];
911 }
912 
913 @end
914 
915 
919 @interface VuoImageGenerator ()
920 @property NSUInteger previousSuggestedPixelsWide;
921 @property NSUInteger previousSuggestedPixelsHigh;
922 @end
923 
924 @implementation VuoImageGenerator
925 @synthesize previousSuggestedPixelsWide;
926 @synthesize previousSuggestedPixelsHigh;
927 
931 + (BOOL)canOpenComposition:(NSURL *)compositionURL
932 {
933  return [self isComposition:compositionURL compliantWithProtocol:VuoProtocol::getProtocol(VuoProtocol::imageGenerator)];
934 }
935 
939 + (BOOL)canOpenCompositionString:(NSString *)compositionString
940 {
941  return [self isCompositionString:compositionString compliantWithProtocol:VuoProtocol::getProtocol(VuoProtocol::imageGenerator)];
942 }
943 
950 - (id)initWithComposition:(NSURL *)aCompositionURL
951 {
952  if (!(self = [super initWithComposition:aCompositionURL protocol:VuoProtocol::getProtocol(VuoProtocol::imageGenerator)]))
953  return nil;
954 
955  [self compileAndRun];
956  if (! self.runner)
957  return nil;
958 
959  self.previousSuggestedPixelsWide = NSUIntegerMax;
960  self.previousSuggestedPixelsHigh = NSUIntegerMax;
961 
962  return self;
963 }
964 
972 - (id)initWithCompositionString:(NSString *)aCompositionString name:(NSString *)name sourcePath:(NSString *)sourcePath
973 {
974  if (!(self = [super initWithCompositionString:aCompositionString name:name sourcePath:sourcePath protocol:VuoProtocol::getProtocol(VuoProtocol::imageGenerator)]))
975  return nil;
976 
977  [self compileAndRun];
978  if (! self.runner)
979  return nil;
980 
981  return self;
982 }
983 
987 - (BOOL)executeWithSuggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time
988 {
989  if ([self isStopped])
990  return NO;
991 
992  VuoRunner::Port *timePort = self.runner->getPublishedInputPortWithName("time");
993  VuoRunner::Port *widthPort = self.runner->getPublishedInputPortWithName("width");
994  VuoRunner::Port *heightPort = self.runner->getPublishedInputPortWithName("height");
995 
996  map<VuoRunner::Port *, json_object *> m;
997 
998  m[timePort] = VuoReal_getJson(time);
999 
1000  if (suggestedPixelsWide != self.previousSuggestedPixelsWide)
1001  {
1002  m[widthPort] = VuoInteger_getJson(suggestedPixelsWide);
1003  self.previousSuggestedPixelsWide = suggestedPixelsWide;
1004  }
1005  if (suggestedPixelsHigh != self.previousSuggestedPixelsHigh)
1006  {
1007  m[heightPort] = VuoInteger_getJson(suggestedPixelsHigh);
1008  self.previousSuggestedPixelsHigh = suggestedPixelsHigh;
1009  }
1010 
1011  self.runner->setPublishedInputPortValues(m);
1012  for (auto &kv : m)
1013  {
1014  self.changedPorts->insert(kv.first);
1015  json_object_put(kv.second);
1016  }
1017 
1018  self.runner->firePublishedInputPortEvent(*self.changedPorts);
1019  self.changedPorts->clear();
1020 
1021  self.runner->waitForFiredPublishedInputPortEvent();
1022  return YES;
1023 }
1024 
1036 - (NSImage *)generateNSImageWithSuggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time
1037 {
1038  if (![self executeWithSuggestedPixelsWide:suggestedPixelsWide pixelsHigh:suggestedPixelsHigh atTime:time])
1039  return nil;
1040  return [self valueForOutputPort:@"outputImage"];
1041 }
1042 
1053 - (GLuint)generateGLTextureWithTarget:(GLuint)target suggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh
1054 {
1055  if (![self executeWithSuggestedPixelsWide:suggestedPixelsWide pixelsHigh:suggestedPixelsHigh atTime:time])
1056  return 0;
1057  return [self glTextureWithTarget:target forOutputPort:@"outputImage" outputPixelsWide:outputPixelsWide pixelsHigh:outputPixelsHigh];
1058 }
1059 
1083 - (GLuint)generateGLTextureWithProvider:(GLuint (^)(NSUInteger pixelsWide, NSUInteger pixelsHigh))provider suggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh ioSurface:(IOSurfaceRef *)outputIOSurface
1084 {
1085  if (![self executeWithSuggestedPixelsWide:suggestedPixelsWide pixelsHigh:suggestedPixelsHigh atTime:time])
1086  return 0;
1087  return [self glTextureFromProvider:provider forOutputPort:@"outputImage" outputPixelsWide:outputPixelsWide pixelsHigh:outputPixelsHigh ioSurface:outputIOSurface];
1088 }
1089 
1098 - (VuoImage)generateVuoImageWithSuggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time
1099 {
1100  if (![self executeWithSuggestedPixelsWide:suggestedPixelsWide pixelsHigh:suggestedPixelsHigh atTime:time])
1101  return NULL;
1102 
1103 
1104  VuoRunner::Port *port = self.runner->getPublishedOutputPortWithName("outputImage");
1105  if (!port)
1106  return NULL;
1107 
1108  json_object *valueJson = self.runner->getPublishedOutputPortValue(port);
1109  VuoImage image = VuoImage_makeFromJson(valueJson);
1110  json_object_put(valueJson);
1111 
1112  return image;
1113 }
1114 
1115 @end