Vuo  2.0.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 
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 
334 - (NSDictionary *)detailsForPort:(NSString *)portName
335 {
336  const char *portNameCString = [portName UTF8String];
337  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName(portNameCString);
338  if (!port)
339  {
340  port = self.runner->getPublishedOutputPortWithName(portNameCString);
341  if (!port)
342  return nil;
343  }
344 
345  NSMutableDictionary *details = [[NSMutableDictionary new] autorelease];
346 
347  details[@"title"] = [NSString stringWithUTF8String:port->getName().c_str()];
348 
349  details[@"type"] = [NSString stringWithUTF8String:port->getType().c_str()];
350 
351  json_object *js = port->getDetails();
352  if (js)
353  {
354  json_object *o = NULL;
355 
356  if (json_object_object_get_ex(js, "default", &o))
357  {
358  id cocoaObject = [VuoRunnerCocoa cocoaObjectWithVuoValue:o ofType:port->getType()];
359  if (cocoaObject)
360  details[@"default"] = cocoaObject;
361  }
362 
363  if (json_object_object_get_ex(js, "suggestedMin", &o))
364  {
365  id cocoaObject = [VuoRunnerCocoa cocoaObjectWithVuoValue:o ofType:port->getType()];
366  if (cocoaObject)
367  details[@"suggestedMin"] = cocoaObject;
368  }
369 
370  if (json_object_object_get_ex(js, "suggestedMax", &o))
371  {
372  id cocoaObject = [VuoRunnerCocoa cocoaObjectWithVuoValue:o ofType:port->getType()];
373  if (cocoaObject)
374  details[@"suggestedMax"] = cocoaObject;
375  }
376 
377  if (json_object_object_get_ex(js, "suggestedStep", &o))
378  {
379  id cocoaObject = [VuoRunnerCocoa cocoaObjectWithVuoValue:o ofType:port->getType()];
380  if (cocoaObject)
381  details[@"suggestedStep"] = cocoaObject;
382  }
383 
384  if (json_object_object_get_ex(js, "menuItems", &o))
385  {
386  NSMutableArray *menuItems = [NSMutableArray new];
387 
388  int itemCount = json_object_array_length(o);
389  for (int i = 0; i < itemCount; ++i)
390  {
391  json_object *item = json_object_array_get_idx(o, i);
392 
393  json_object *value;
394  json_object_object_get_ex(item, "value", &value);
395  json_object *name;
396  json_object_object_get_ex(item, "name", &name);
397 
398  const char *key = json_object_get_string(value);
399  const char *summary = json_object_get_string(name);
400  NSDictionary *menuItem = @{
401  @"value": [NSString stringWithUTF8String:key],
402  @"name": [NSString stringWithUTF8String:summary],
403  };
404  [menuItems addObject:menuItem];
405  }
406 
407  details[@"menuItems"] = menuItems;
408  [menuItems release];
409  }
410  }
411 
412  return details;
413 }
414 
419 {
420  if ([self isStopped])
421  return nil;
422 
423  NSMutableDictionary *properties = [[NSMutableDictionary new] autorelease];
424 
425  vector<VuoRunner::Port *> ports = self.runner->getPublishedInputPorts();
426  for (vector<VuoRunner::Port *>::iterator it = ports.begin(); it != ports.end(); ++it)
427  if (self.protocol && !self.protocol->hasInputPort((*it)->getName()))
428  {
429  json_object *portValue = self.runner->getPublishedInputPortValue(*it);
430  const char *valueAsString = json_object_to_json_string_ext(portValue, JSON_C_TO_STRING_PLAIN);
431  properties[[NSString stringWithUTF8String:(*it)->getName().c_str()]] = [NSString stringWithUTF8String:valueAsString];
432  }
433 
434  return properties;
435 }
436 
443 - (BOOL)setInputValuesWithPropertyList:(id)propertyList
444 {
445  if ([self isStopped])
446  return NO;
447 
448  __block BOOL ok = YES;
449  __block map<VuoRunner::Port *, json_object *> m;
450  [(NSDictionary *)propertyList enumerateKeysAndObjectsUsingBlock:^(NSString *portName, NSString *value, BOOL *stop) {
451  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName([portName UTF8String]);
452  if (!port)
453  {
454  ok = NO;
455  return;
456  }
457 
458  json_object *valueJson = json_tokener_parse([value UTF8String]);
459  if (!valueJson)
460  {
461  ok = NO;
462  return;
463  }
464 
465  m[port] = valueJson;
466  }];
467 
468  runner->setPublishedInputPortValues(m);
469 
470  for (auto &kv : m)
471  {
472  self.changedPorts->insert(kv.first);
473  json_object_put(kv.second);
474  }
475 
476  return ok;
477 }
478 
494 - (BOOL)setInputValues:(NSDictionary *)namesAndValues
495 {
496  if ([self isStopped])
497  return NO;
498 
499  BOOL ok = YES;
500  map<VuoRunner::Port *, json_object *> m;
501  for (NSString *portName in namesAndValues)
502  {
503  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName([portName UTF8String]);
504  if (!port)
505  {
506  ok = NO;
507  continue;
508  }
509 
510  id value = namesAndValues[portName];
512  if (!valueJson)
513  {
514  ok = NO;
515  continue;
516  }
517 
518  m[port] = valueJson;
519  }
520 
521  self.runner->setPublishedInputPortValues(m);
522 
523  for (auto &kv : m)
524  {
525  self.changedPorts->insert(kv.first);
526  json_object_put(kv.second);
527  }
528 
529  return ok;
530 }
531 
539 - (BOOL)setInputJSON:(NSDictionary *)namesAndJSON
540 {
541  if ([self isStopped])
542  return NO;
543 
544  BOOL ok = YES;
545  map<VuoRunner::Port *, json_object *> m;
546  for (NSString *portName in namesAndJSON)
547  {
548  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName([portName UTF8String]);
549  if (!port)
550  {
551  ok = NO;
552  continue;
553  }
554 
555  id nsValue = namesAndJSON[portName];
556  json_object *value = (json_object *)[nsValue pointerValue];
557 
558  // If we're feeding a string to a VuoImage port, assume it's an image URL, and try to load it.
559  if (port->getType() == "VuoImage")
560  {
561  if (json_object_get_type(value) == json_type_string)
562  {
563  char *s = strdup(json_object_get_string(value));
564  VuoImage image = VuoImage_get(s);
565  if (!image)
566  {
567  NSLog(@"Error: Couldn't load image '%s'.\n", s);
568  free(s);
569  ok = NO;
570  continue;
571  }
572  free(s);
573 
574  m[port] = VuoImage_getInterprocessJson(image);
575  }
576  }
577  else
578  m[port] = value;
579  }
580 
581  self.runner->setPublishedInputPortValues(m);
582 
583  for (auto &kv : m)
584  self.changedPorts->insert(kv.first);
585 
586  return ok;
587 }
588 
606 - (id)valueForOutputPort:(NSString *)portName
607 {
608  if ([self isStopped])
609  return nil;
610 
611  VuoRunner::Port *port = self.runner->getPublishedOutputPortWithName([portName UTF8String]);
612  if (!port)
613  return nil;
614 
615  json_object *valueJson = self.runner->getPublishedOutputPortValue(port);
616  return [VuoRunnerCocoa cocoaObjectWithVuoValue:valueJson ofType:port->getType()];
617 }
618 
628 - (GLuint)glTextureWithTarget:(GLuint)target forOutputPort:(NSString *)portName outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh
629 {
630  if ([self isStopped])
631  return 0;
632 
633  VuoRunner::Port *port = self.runner->getPublishedOutputPortWithName([portName UTF8String]);
634  if (!port)
635  return 0;
636 
637  json_object *valueJson = self.runner->getPublishedOutputPortValue(port);
638  VuoImage vuoImage = VuoImage_makeFromJson(valueJson);
639  if (!vuoImage)
640  return 0;
641  json_object_put(valueJson);
642 
643  if (target == GL_TEXTURE_RECTANGLE_ARB)
644  {
645  VuoRetain(vuoImage);
646  VuoImage newVuoImage = VuoImage_makeGlTextureRectangleCopy(vuoImage);
647  VuoRelease(vuoImage);
648  vuoImage = newVuoImage;
649  }
650 
651  if (outputPixelsWide)
652  *outputPixelsWide = vuoImage->pixelsWide;
653  if (outputPixelsHigh)
654  *outputPixelsHigh = vuoImage->pixelsHigh;
655 
657 
658  return vuoImage->glTextureName;
659 }
660 
680 - (GLuint)glTextureFromProvider:(GLuint (^)(NSUInteger pixelsWide, NSUInteger pixelsHigh))provider forOutputPort:(NSString *)portName outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh ioSurface:(IOSurfaceRef *)outputIOSurface
681 {
682  if ([self isStopped])
683  return 0;
684 
685  VuoRunner::Port *port = self.runner->getPublishedOutputPortWithName([portName UTF8String]);
686  if (!port)
687  return 0;
688 
689  json_object *valueJson = self.runner->getPublishedOutputPortValue(port);
690 
691  GLuint (^provider2)(unsigned int, unsigned int) = ^GLuint(unsigned int pixelsWide, unsigned int pixelsHigh){
692  return provider(pixelsWide, pixelsHigh);
693  };
694  unsigned int outputPixelsWide2, outputPixelsHigh2;
695  GLuint outputTexture = VuoImage_resolveInterprocessJsonUsingTextureProvider(valueJson, provider2, &outputPixelsWide2, &outputPixelsHigh2, outputIOSurface);
696  *outputPixelsWide = outputPixelsWide2;
697  *outputPixelsHigh = outputPixelsHigh2;
698  return outputTexture;
699 }
700 
701 @end
702 
703 
704 @implementation VuoImageFilter
708 + (BOOL)canOpenComposition:(NSURL *)compositionURL
709 {
710  return [self isComposition:compositionURL compliantWithProtocol:VuoProtocol::getProtocol(VuoProtocol::imageFilter)];
711 }
712 
716 + (BOOL)canOpenCompositionString:(NSString *)compositionString
717 {
718  return [self isCompositionString:compositionString compliantWithProtocol:VuoProtocol::getProtocol(VuoProtocol::imageFilter)];
719 }
720 
727 - (id)initWithComposition:(NSURL *)aCompositionURL
728 {
729  if (!(self = [super initWithComposition:aCompositionURL protocol:VuoProtocol::getProtocol(VuoProtocol::imageFilter)]))
730  return nil;
731 
732  [self compileAndRun];
733  if (! self.runner)
734  return nil;
735 
736  return self;
737 }
738 
746 - (id)initWithCompositionString:(NSString *)aCompositionString name:(NSString *)name sourcePath:(NSString *)sourcePath
747 {
748  if (!(self = [super initWithCompositionString:aCompositionString name:name sourcePath:sourcePath protocol:VuoProtocol::getProtocol(VuoProtocol::imageFilter)]))
749  return nil;
750 
751  [self compileAndRun];
752  if (! self.runner)
753  return nil;
754 
755  return self;
756 }
757 
761 static void VuoRunnerCocoa_doNothingCallback(VuoImage imageToFree)
762 {
763 }
764 
768 - (BOOL)executeWithGLTexture:(GLuint)textureName target:(GLuint)target pixelsWide:(unsigned long)pixelsWide pixelsHigh:(unsigned long)pixelsHigh atTime:(NSTimeInterval)time
769 {
770  if ([self isStopped])
771  return NO;
772 
773  {
774  VuoRunner::Port *port = self.runner->getPublishedInputPortWithName("image");
775  VuoImage image = VuoImage_makeClientOwned(textureName, GL_RGBA, pixelsWide, pixelsHigh, VuoRunnerCocoa_doNothingCallback, NULL);
776  image->glTextureTarget = target;
777  VuoRetain(image);
778  json_object *valueJson = VuoImage_getInterprocessJson(image);
779  VuoRelease(image);
780  map<VuoRunner::Port *, json_object *> m;
781  m[port] = valueJson;
782  self.runner->setPublishedInputPortValues(m);
783  json_object_put(valueJson);
784 
785  self.changedPorts->insert(port);
786  }
787 
788  return [self executeAtTime:time];
789 }
790 
794 - (BOOL)executeAtTime:(NSTimeInterval)time
795 {
796  if ([self isStopped])
797  return NO;
798 
799  VuoRunner::Port *timePort = self.runner->getPublishedInputPortWithName("time");
800 
801  {
802  json_object *valueJson = VuoReal_getJson(time);
803  map<VuoRunner::Port *, json_object *> m;
804  m[timePort] = valueJson;
805  self.runner->setPublishedInputPortValues(m);
806  json_object_put(valueJson);
807 
808  self.changedPorts->insert(timePort);
809  }
810 
811  self.runner->firePublishedInputPortEvent(*self.changedPorts);
812  self.changedPorts->clear();
813 
814  self.runner->waitForFiredPublishedInputPortEvent();
815  return YES;
816 }
817 
831 - (NSImage *)filterNSImage:(NSImage *)image atTime:(NSTimeInterval)time
832 {
833  [self setInputValues:@{@"image":image}];
834 
835  if (![self executeAtTime:time])
836  return nil;
837 
838  return [self valueForOutputPort:@"outputImage"];
839 }
840 
855 - (GLuint)filterGLTexture:(GLuint)textureName target:(GLuint)target pixelsWide:(NSUInteger)pixelsWide pixelsHigh:(NSUInteger)pixelsHigh atTime:(NSTimeInterval)time outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh
856 {
857  if (![self executeWithGLTexture:textureName target:target pixelsWide:pixelsWide pixelsHigh:pixelsHigh atTime:time])
858  return 0;
859  return [self glTextureWithTarget:target forOutputPort:@"outputImage" outputPixelsWide:outputPixelsWide pixelsHigh:outputPixelsHigh];
860 }
861 
891 - (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
892 {
893  if (![self executeWithGLTexture:textureName target:target pixelsWide:pixelsWide pixelsHigh:pixelsHigh atTime:time])
894  return 0;
895 
896  return [self glTextureFromProvider:provider forOutputPort:@"outputImage" outputPixelsWide:outputPixelsWide pixelsHigh:outputPixelsHigh ioSurface:outputIOSurface];
897 }
898 
899 @end
900 
901 
905 @interface VuoImageGenerator ()
906 @property NSUInteger previousSuggestedPixelsWide;
907 @property NSUInteger previousSuggestedPixelsHigh;
908 @end
909 
910 @implementation VuoImageGenerator
911 @synthesize previousSuggestedPixelsWide;
912 @synthesize previousSuggestedPixelsHigh;
913 
917 + (BOOL)canOpenComposition:(NSURL *)compositionURL
918 {
919  return [self isComposition:compositionURL compliantWithProtocol:VuoProtocol::getProtocol(VuoProtocol::imageGenerator)];
920 }
921 
925 + (BOOL)canOpenCompositionString:(NSString *)compositionString
926 {
927  return [self isCompositionString:compositionString compliantWithProtocol:VuoProtocol::getProtocol(VuoProtocol::imageGenerator)];
928 }
929 
936 - (id)initWithComposition:(NSURL *)aCompositionURL
937 {
938  if (!(self = [super initWithComposition:aCompositionURL protocol:VuoProtocol::getProtocol(VuoProtocol::imageGenerator)]))
939  return nil;
940 
941  [self compileAndRun];
942  if (! self.runner)
943  return nil;
944 
945  self.previousSuggestedPixelsWide = NSUIntegerMax;
946  self.previousSuggestedPixelsHigh = NSUIntegerMax;
947 
948  return self;
949 }
950 
958 - (id)initWithCompositionString:(NSString *)aCompositionString name:(NSString *)name sourcePath:(NSString *)sourcePath
959 {
960  if (!(self = [super initWithCompositionString:aCompositionString name:name sourcePath:sourcePath protocol:VuoProtocol::getProtocol(VuoProtocol::imageGenerator)]))
961  return nil;
962 
963  [self compileAndRun];
964  if (! self.runner)
965  return nil;
966 
967  return self;
968 }
969 
973 - (BOOL)executeWithSuggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time
974 {
975  if ([self isStopped])
976  return NO;
977 
978  VuoRunner::Port *timePort = self.runner->getPublishedInputPortWithName("time");
979  VuoRunner::Port *widthPort = self.runner->getPublishedInputPortWithName("width");
980  VuoRunner::Port *heightPort = self.runner->getPublishedInputPortWithName("height");
981 
982  map<VuoRunner::Port *, json_object *> m;
983 
984  m[timePort] = VuoReal_getJson(time);
985 
986  if (suggestedPixelsWide != self.previousSuggestedPixelsWide)
987  {
988  m[widthPort] = VuoInteger_getJson(suggestedPixelsWide);
989  self.previousSuggestedPixelsWide = suggestedPixelsWide;
990  }
991  if (suggestedPixelsHigh != self.previousSuggestedPixelsHigh)
992  {
993  m[heightPort] = VuoInteger_getJson(suggestedPixelsHigh);
994  self.previousSuggestedPixelsHigh = suggestedPixelsHigh;
995  }
996 
997  self.runner->setPublishedInputPortValues(m);
998  for (auto &kv : m)
999  {
1000  self.changedPorts->insert(kv.first);
1001  json_object_put(kv.second);
1002  }
1003 
1004  self.runner->firePublishedInputPortEvent(*self.changedPorts);
1005  self.changedPorts->clear();
1006 
1007  self.runner->waitForFiredPublishedInputPortEvent();
1008  return YES;
1009 }
1010 
1022 - (NSImage *)generateNSImageWithSuggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time
1023 {
1024  if (![self executeWithSuggestedPixelsWide:suggestedPixelsWide pixelsHigh:suggestedPixelsHigh atTime:time])
1025  return nil;
1026  return [self valueForOutputPort:@"outputImage"];
1027 }
1028 
1039 - (GLuint)generateGLTextureWithTarget:(GLuint)target suggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time outputPixelsWide:(NSUInteger *)outputPixelsWide pixelsHigh:(NSUInteger *)outputPixelsHigh
1040 {
1041  if (![self executeWithSuggestedPixelsWide:suggestedPixelsWide pixelsHigh:suggestedPixelsHigh atTime:time])
1042  return 0;
1043  return [self glTextureWithTarget:target forOutputPort:@"outputImage" outputPixelsWide:outputPixelsWide pixelsHigh:outputPixelsHigh];
1044 }
1045 
1069 - (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
1070 {
1071  if (![self executeWithSuggestedPixelsWide:suggestedPixelsWide pixelsHigh:suggestedPixelsHigh atTime:time])
1072  return 0;
1073  return [self glTextureFromProvider:provider forOutputPort:@"outputImage" outputPixelsWide:outputPixelsWide pixelsHigh:outputPixelsHigh ioSurface:outputIOSurface];
1074 }
1075 
1084 - (VuoImage)generateVuoImageWithSuggestedPixelsWide:(NSUInteger)suggestedPixelsWide pixelsHigh:(NSUInteger)suggestedPixelsHigh atTime:(NSTimeInterval)time
1085 {
1086  if (![self executeWithSuggestedPixelsWide:suggestedPixelsWide pixelsHigh:suggestedPixelsHigh atTime:time])
1087  return NULL;
1088 
1089 
1090  VuoRunner::Port *port = self.runner->getPublishedOutputPortWithName("outputImage");
1091  if (!port)
1092  return NULL;
1093 
1094  json_object *valueJson = self.runner->getPublishedOutputPortValue(port);
1095  VuoImage image = VuoImage_makeFromJson(valueJson);
1096  json_object_put(valueJson);
1097 
1098  return image;
1099 }
1100 
1101 @end