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