Vuo  2.1.2
VuoRunnerCocoa+Conversion.mm
Go to the documentation of this file.
1 
11 
12 #include <OpenGL/CGLMacro.h>
13 #include <QuartzCore/CoreImage.h>
14 #include <QuartzCore/CoreVideo.h>
15 
16 #pragma clang diagnostic push
17 #pragma clang diagnostic ignored "-Wdocumentation"
18 #include <json-c/json.h>
19 #pragma clang diagnostic pop
20 
21 #include <dlfcn.h>
22 #include <vector>
23 #include "VuoStringUtilities.hh"
24 #include "VuoType.hh"
25 
26 extern "C" {
27 #include "VuoHeap.h"
28 #include "VuoGlContext.h"
29 
30 #include "../node/node.h"
31 }
32 
33 @implementation VuoRunnerCocoa (Conversion)
34 
40 + (id)cocoaObjectWithVuoValue:(json_object *)vuoValue ofType:(string)type
41 {
42  if (type == "VuoBoolean")
43  {
44  VuoBoolean v = VuoBoolean_makeFromJson(vuoValue);
45  return [NSNumber numberWithBool:v];
46  }
47  if (type == "VuoList_VuoBoolean")
48  {
50  unsigned long count = VuoListGetCount_VuoBoolean(l);
51  NSMutableArray *a = [[NSMutableArray new] autorelease];
52  for (unsigned long i=0; i<count; ++i)
53  [a addObject:[NSNumber numberWithBool:VuoListGetValue_VuoBoolean(l, i+1)]];
54  return a;
55  }
56 
57  if (type == "VuoInteger")
58  {
59  VuoInteger v = VuoInteger_makeFromJson(vuoValue);
60  return [NSNumber numberWithLong:v];
61  }
62  if (type == "VuoList_VuoInteger")
63  {
65  unsigned long count = VuoListGetCount_VuoInteger(l);
66  NSMutableArray *a = [[NSMutableArray new] autorelease];
67  for (unsigned long i=0; i<count; ++i)
68  [a addObject:[NSNumber numberWithLong:VuoListGetValue_VuoInteger(l, i+1)]];
69  return a;
70  }
71 
72  if (type == "VuoReal")
73  {
74  VuoReal v = VuoReal_makeFromJson(vuoValue);
75  return [NSNumber numberWithDouble:v];
76  }
77  if (type == "VuoList_VuoReal")
78  {
80  unsigned long count = VuoListGetCount_VuoReal(l);
81  NSMutableArray *a = [[NSMutableArray new] autorelease];
82  for (unsigned long i=0; i<count; ++i)
83  [a addObject:[NSNumber numberWithDouble:VuoListGetValue_VuoReal(l, i+1)]];
84  return a;
85  }
86 
87  if (type == "VuoText")
88  {
89  VuoText v = VuoText_makeFromJson(vuoValue);
90  VuoRetain(v);
91  NSString *s = [NSString stringWithUTF8String:v];
92  VuoRelease(v);
93  return s;
94  }
95  if (type == "VuoList_VuoText")
96  {
98  unsigned long count = VuoListGetCount_VuoText(l);
99  NSMutableArray *a = [[NSMutableArray new] autorelease];
100  for (unsigned long i=0; i<count; ++i)
101  [a addObject:[NSString stringWithUTF8String:VuoListGetValue_VuoText(l, i+1)]];
102  return a;
103  }
104 
105  if (type == "VuoColor")
106  {
107  VuoColor v = VuoColor_makeFromJson(vuoValue);
108  CGFloat c[4] = {v.r, v.g, v.b, v.a};
109  return [NSColor colorWithColorSpace:[NSColorSpace sRGBColorSpace] components:c count:4];
110  }
111  if (type == "VuoList_VuoColor")
112  {
114  unsigned long count = VuoListGetCount_VuoColor(l);
115  NSMutableArray *a = [[NSMutableArray new] autorelease];
116  for (unsigned long i=0; i<count; ++i)
117  {
119  CGFloat c[4] = {v.r, v.g, v.b, v.a};
120  [a addObject:[NSColor colorWithColorSpace:[NSColorSpace sRGBColorSpace] components:c count:4]];
121  }
122  return a;
123  }
124 
125  if (type == "VuoImage")
126  {
127  VuoImage v = VuoImage_makeFromJson(vuoValue);
128  VuoRetain(v);
129  NSImage *im = [self nsImageWithVuoImage:v];
130  VuoRelease(v);
131  return im;
132  }
133  if (type == "VuoList_VuoImage")
134  {
136  unsigned long count = VuoListGetCount_VuoImage(l);
137  NSMutableArray *a = [[NSMutableArray new] autorelease];
138  for (unsigned long i=0; i<count; ++i)
139  {
141  VuoRetain(v);
142  [a addObject:[self nsImageWithVuoImage:v]];
143  VuoRelease(v);
144  }
145  return a;
146  }
147 
148  if (type == "VuoPoint2d")
149  {
150  VuoPoint2d v = VuoPoint2d_makeFromJson(vuoValue);
151  return [NSValue valueWithPoint:NSMakePoint(v.x, v.y)];
152  }
153  if (type == "VuoList_VuoPoint2d")
154  {
156  unsigned long count = VuoListGetCount_VuoPoint2d(l);
157  NSMutableArray *a = [[NSMutableArray new] autorelease];
158  for (unsigned long i=0; i<count; ++i)
159  {
160  VuoPoint2d v = VuoListGetValue_VuoPoint2d(l, i+1);
161  [a addObject:[NSValue valueWithPoint:NSMakePoint(v.x, v.y)]];
162  }
163  return a;
164  }
165 
166  if (type == "VuoPoint3d")
167  {
168  VuoPoint3d v = VuoPoint3d_makeFromJson(vuoValue);
169  size_t s = sizeof(double)*3;
170  double *d = (double *)malloc(s);
171  d[0] = v.x;
172  d[1] = v.y;
173  d[2] = v.z;
174  return [NSData dataWithBytesNoCopy:d length:s];
175  }
176  if (type == "VuoList_VuoPoint3d")
177  {
179  unsigned long count = VuoListGetCount_VuoPoint3d(l);
180  NSMutableArray *a = [[NSMutableArray new] autorelease];
181  for (unsigned long i=0; i<count; ++i)
182  {
183  VuoPoint3d v = VuoListGetValue_VuoPoint3d(l, i+1);
184  size_t s = sizeof(double)*3;
185  double *d = (double *)malloc(s);
186  d[0] = v.x;
187  d[1] = v.y;
188  d[2] = v.z;
189  [a addObject:[NSData dataWithBytesNoCopy:d length:s]];
190  }
191  return a;
192  }
193 
194  // Maybe it's a list of enum values?
196  {
198  string allowedValuesFunctionName = itemType + "_getAllowedValues";
199  typedef void *(*allowedValuesFunctionType)(void);
200  allowedValuesFunctionType allowedValuesFunction = (allowedValuesFunctionType)dlsym(RTLD_SELF, allowedValuesFunctionName.c_str());
201  if (allowedValuesFunction)
202  if (json_object_is_type(vuoValue, json_type_array))
203  {
204  unsigned long arrayLength = json_object_array_length(vuoValue);
205  if (!arrayLength)
206  return [[NSArray new] autorelease];
207 
208  else if (json_object_is_type(json_object_array_get_idx(vuoValue, 0), json_type_string))
209  {
210  NSMutableArray *a = [[NSMutableArray new] autorelease];
211  for (unsigned long i = 0; i < arrayLength; ++i)
212  [a addObject:[NSString stringWithUTF8String:json_object_get_string(json_object_array_get_idx(vuoValue, i))]];
213  return a;
214  }
215  }
216  }
217 
218  // Maybe it's an enum?
219  string allowedValuesFunctionName = type + "_getAllowedValues";
220  typedef void *(*allowedValuesFunctionType)(void);
221  allowedValuesFunctionType allowedValuesFunction = (allowedValuesFunctionType)dlsym(RTLD_SELF, allowedValuesFunctionName.c_str());
222  if (allowedValuesFunction)
223  if (json_object_is_type(vuoValue, json_type_string))
224  return [NSString stringWithUTF8String:json_object_get_string(vuoValue)];
225 
226  VUserLog("Type %s isn't supported yet. Please https://vuo.org/contact us if you'd like support for this type.", type.c_str());
227  return nil;
228 }
229 
233 static void VuoRunnerCocoa_doNothingCallback(VuoImage imageToFree)
234 {
235 }
236 
242 + (json_object *)vuoValueWithCocoaObject:(id)value
243 {
244  if (!value)
245  return NULL;
246 
247  // Is this a Vuo-compatible NSObject (or CFType that's toll-free bridged to one)?
248  if ([value respondsToSelector:@selector(vuoValue)])
249  return [value vuoValue];
250 
251  // Is this a (non-toll-free bridged) CFType?
252  if (strcmp(object_getClassName(value), "__NSCFType") == 0)
253  {
254  CFTypeID type = CFGetTypeID(value);
255 
256  if (type == CGColorGetTypeID())
257  {
258  NSColorSpace *nscs = [[NSColorSpace alloc] initWithCGColorSpace:CGColorGetColorSpace((CGColorRef)value)];
259  NSColor *color = [NSColor colorWithColorSpace:nscs components:CGColorGetComponents((CGColorRef)value) count:CGColorGetNumberOfComponents((CGColorRef)value)];
260  [nscs release];
261  return [color vuoValue];
262  }
263 
264  if (type == CGImageGetTypeID())
265  {
266  CGImageRef cgimage = (CGImageRef)value;
267 
268  if (CGImageGetAlphaInfo(cgimage) != kCGImageAlphaPremultipliedLast)
269  {
270  VUserLog("Error: Alpha option %d isn't supported yet. Please use kCGImageAlphaPremultipliedLast, and https://vuo.org/contact us if you'd like support for other alpha options.", CGImageGetAlphaInfo(cgimage));
271  return NULL;
272  }
273 
274  if (CGImageGetBitmapInfo(cgimage) & kCGBitmapFloatComponents)
275  {
276  VUserLog("Error: Floating-point pixel values aren't supported yet. Please use integer pixel values, and https://vuo.org/contact us if you'd like support for floating-point pixel values.");
277  return NULL;
278  }
279 
280  if (CGImageGetBitsPerComponent(cgimage) != 8)
281  {
282  VUserLog("Error: BitsPerComponent=%lu isn't supported yet. Please use 8 bits per component, and https://vuo.org/contact us if you'd like support for other bit depths.", CGImageGetBitsPerComponent(cgimage));
283  return NULL;
284  }
285 
286  if (CGImageGetBitsPerPixel(cgimage) != 32)
287  {
288  VUserLog("Error: BitsPerPixel=%lu isn't supported yet. Please use 32 bits per pixel, and https://vuo.org/contact us if you'd like support for other bit depths.", CGImageGetBitsPerPixel(cgimage));
289  return NULL;
290  }
291 
292  unsigned long width = CGImageGetWidth(cgimage);
293  unsigned long height = CGImageGetHeight(cgimage);
294  if (CGImageGetBytesPerRow(cgimage) != width*4)
295  {
296  VUserLog("Error: BytesPerRow must be width*4. https://vuo.org/contact us if you'd like support for inexact strides.");
297  return NULL;
298  }
299 
300 // NSString *colorSpaceName = (NSString *)CGColorSpaceCopyName(CGImageGetColorSpace(cgimage));
301 // if (![colorSpaceName isEqualToString:(NSString *)kCGColorSpaceSRGB])
302 // {
303 // VUserLog("Error: Colorspace '%s' isn't supported yet. Please use kCGColorSpaceSRGB, and https://vuo.org/contact us if you'd like support for other colorspaces.", [colorSpaceName UTF8String]);
304 // [colorSpaceName release];
305 // return NULL;
306 // }
307 // CFRelease(colorSpaceName);
308 
309  CGDataProviderRef provider = CGImageGetDataProvider(cgimage);
310  CFDataRef data = CGDataProviderCopyData(provider);
311 
312  char *bitmapData = (char *)CFDataGetBytePtr(data);
313  int bytesPerRow = width*4;
314  char *flippedBitmapData = (char *)malloc(bytesPerRow*height);
315 
316  // Flip the image data (CGImage is unflipped, but VuoImage_makeFromBuffer() expects flipped).
317  for (unsigned long y = 0; y < height; ++y)
318  memcpy(flippedBitmapData + bytesPerRow * (height - y - 1), bitmapData + bytesPerRow * y, bytesPerRow);
319 
320  CFRelease(data);
321 
322  VuoImage vi = VuoImage_makeFromBuffer(flippedBitmapData, GL_RGBA, width, height, VuoImageColorDepth_8, ^(void *buffer){ free(flippedBitmapData); });
323  VuoRetain(vi);
324 
326  VuoRelease(vi);
327  return js;
328  }
329 
330  if (type == CVPixelBufferGetTypeID())
331  {
332  CVPixelBufferRef cvpb = (CVPixelBufferRef)value;
333 
334  if (CVPixelBufferIsPlanar(cvpb))
335  {
336  VUserLog("Error: Planar buffers aren't supported yet. Please use chunky buffers, and https://vuo.org/contact us if you'd like support for planar buffers.");
337  return NULL;
338  }
339 
340  if (CVPixelBufferGetPixelFormatType(cvpb) != kCVPixelFormatType_32BGRA)
341  {
342  unsigned long pf = CVPixelBufferGetPixelFormatType(cvpb);
343  VUserLog("Error: Pixel format %lu isn't supported yet. Please use kCVPixelFormatType_32BGRA, and https://vuo.org/contact us if you'd like support for other pixel formats.", pf);
344  return NULL;
345  }
346 
347  unsigned long width = CVPixelBufferGetWidth(cvpb);
348  unsigned long height = CVPixelBufferGetHeight(cvpb);
349  if (CVPixelBufferGetBytesPerRow(cvpb) != width*4)
350  {
351  VUserLog("Error: BytesPerRow must be width*4. https://vuo.org/contact us if you'd like support for inexact strides.");
352  return NULL;
353  }
354 
355  CVReturn ret = CVPixelBufferLockBaseAddress(cvpb, kCVPixelBufferLock_ReadOnly);
356  if (ret != kCVReturnSuccess)
357  {
358  VUserLog("CVPixelBufferLockBaseAddress() failed: %d", ret);
359  return NULL;
360  }
361 
362  const unsigned char *pixels = (const unsigned char *)CVPixelBufferGetBaseAddress(cvpb);
363  if (!pixels)
364  {
365  VUserLog("Error: CVPixelBufferGetBaseAddress() returned NULL.");
366  return NULL;
367  }
368 
369  CVPixelBufferRetain(cvpb);
370  VuoImage vi = VuoImage_makeFromBuffer(pixels, GL_BGRA, width, height, VuoImageColorDepth_8, ^(void *buffer){ CVPixelBufferRelease(cvpb); });
371  VuoRetain(vi);
372 
373  ret = CVPixelBufferUnlockBaseAddress(cvpb, kCVPixelBufferLock_ReadOnly);
374  if (ret != kCVReturnSuccess)
375  {
376  VUserLog("CVPixelBufferUnlockBaseAddress() failed: %d", ret);
377  return NULL;
378  }
379 
381  VuoRelease(vi);
382  return js;
383  }
384 
385  if (type == CVOpenGLTextureGetTypeID())
386  {
387  CVOpenGLTextureRef cvgl = (CVOpenGLTextureRef)value;
388 
389  GLuint name = CVOpenGLTextureGetName(cvgl);
390  if (!name)
391  {
392  VUserLog("Error: CVOpenGLTextureGetName() returned 0.");
393  return NULL;
394  }
395 
396  GLfloat lowerLeft[2];
397  GLfloat lowerRight[2];
398  GLfloat upperRight[2];
399  GLfloat upperLeft[2];
400  CVOpenGLTextureGetCleanTexCoords(cvgl, lowerLeft, lowerRight, upperRight, upperLeft);
401 
402  unsigned int width = abs((int)(lowerRight[0] - lowerLeft[0]));
403  unsigned int height = abs((int)(upperLeft[1] - lowerLeft[1]));
404 // VLog("%dx%d",width,height);
405 
406  VuoImage vi;
407  GLenum target = CVOpenGLTextureGetTarget(cvgl);
408  if (target == GL_TEXTURE_2D)
409  vi = VuoImage_makeClientOwned(name, GL_RGBA, width, height, VuoRunnerCocoa_doNothingCallback, NULL);
410  else if (target == GL_TEXTURE_RECTANGLE_ARB)
411  vi = VuoImage_makeClientOwnedGlTextureRectangle(name, GL_RGBA, width, height, VuoRunnerCocoa_doNothingCallback, NULL);
412  else
413  {
414  VUserLog("Error: GL texture target %d isn't supported yet. Please use GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB, and https://vuo.org/contact us if you'd like support for other targets.", target);
415  return NULL;
416  }
417 
418  VuoRetain(vi);
420  VuoRelease(vi);
421  return js;
422  }
423 
424  NSString *typeDescription = (NSString *)CFCopyTypeIDDescription(type);
425  VUserLog("Error: Unknown CFType '%s'", [typeDescription UTF8String]);
426  [typeDescription release];
427  return NULL;
428  }
429 
430  VUserLog("Error: Unknown type '%s' for object '%s'", object_getClassName(value), [[value description] UTF8String]);
431  return NULL;
432 }
433 
437 + (NSImage *)nsImageWithVuoImage:(VuoImage)vi
438 {
439  if (!vi)
440  return nil;
441 
442  // Allocate memory to store the image data.
443  unsigned int bytesPerRow = 4 * vi->pixelsWide;
444  NSBitmapImageRep *nbi = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
445  pixelsWide:vi->pixelsWide
446  pixelsHigh:vi->pixelsHigh
447  bitsPerSample:8
448  samplesPerPixel:4
449  hasAlpha:YES
450  isPlanar:NO
451  colorSpaceName:NSDeviceRGBColorSpace
452  bytesPerRow:bytesPerRow
453  bitsPerPixel:0];
454  if (!nbi)
455  return nil;
456 
457  // Download the image data from the GPU.
458  unsigned char *bitmapData = [nbi bitmapData];
459  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
460  glBindTexture(GL_TEXTURE_2D, vi->glTextureName);
461  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmapData);
462  glBindTexture(GL_TEXTURE_2D, 0);
463  });
464 
465  // Flip the image data (OpenGL returns flipped data, but NSBitmapImageRep assumes it is not flipped).
466  unsigned char *tmp = (unsigned char *)malloc(bytesPerRow);
467  for (unsigned long y = 0; y < vi->pixelsHigh / 2; ++y)
468  {
469  memcpy(tmp, bitmapData + bytesPerRow * y, bytesPerRow);
470  memcpy(bitmapData + bytesPerRow * y, bitmapData + bytesPerRow * (vi->pixelsHigh - y - 1), bytesPerRow);
471  memcpy(bitmapData + bytesPerRow * (vi->pixelsHigh - y - 1), tmp, bytesPerRow);
472  }
473  free(tmp);
474 
475  NSImage *ni = [[NSImage alloc] initWithSize:[nbi size]];
476  [ni addRepresentation:nbi];
477  [nbi release];
478  return [ni autorelease];
479 }
480 
481 @end
482 
490 {
491  char type = [self objCType][0];
492 
493  if (type == 'B')
494  return VuoBoolean_getJson([self boolValue]);
495 
496  if (type == 'f' || type == 'd')
497  return VuoReal_getJson([self doubleValue]);
498 
499  if (type == 'c' || type == 'i' || type == 's' || type == 'l' || type == 'q'
500  || type == 'C' || type == 'I' || type == 'S' || type == 'L' || type == 'Q')
501  return VuoInteger_getJson([self longLongValue]);
502 
503  VUserLog("Error: Unknown type '%s'", [self objCType]);
504  return NULL;
505 }
506 @end
507 
515 {
516  return VuoText_getJson([self UTF8String]);
517 }
518 @end
519 
527 {
528  CGFloat c[4] = {0,0,0,1};
529  NSColor *color = [self colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
530  [color getComponents:c];
531  return VuoColor_getJson(VuoColor_makeWithRGBA(c[0], c[1], c[2], c[3]));
532 }
533 @end
534 
542 {
543  NSColorSpace *nscs = [[NSColorSpace alloc] initWithCGColorSpace:[self colorSpace]];
544  NSColor *color = [NSColor colorWithColorSpace:nscs components:[self components] count:[self numberOfComponents]];
545  [nscs release];
546  return [color vuoValue];
547 }
548 @end
549 
557 {
558  float scale = [self recommendedLayerContentsScale:0];
559  unsigned int pixelsWide = [self size].width * scale;
560  unsigned int pixelsHigh = [self size].height * scale;
561 
562  NSBitmapImageRep *nbir = [[NSBitmapImageRep alloc]
563  initWithBitmapDataPlanes: NULL
564  pixelsWide:pixelsWide
565  pixelsHigh:pixelsHigh
566  bitsPerSample:8
567  samplesPerPixel:4
568  hasAlpha:YES
569  isPlanar:NO
570  colorSpaceName:NSDeviceRGBColorSpace
571  bytesPerRow:pixelsWide*4
572  bitsPerPixel:32];
573 
574  NSGraphicsContext *ngc = [NSGraphicsContext graphicsContextWithBitmapImageRep:nbir];
575  [NSGraphicsContext saveGraphicsState];
576  [NSGraphicsContext setCurrentContext:ngc];
577  [self drawInRect:NSMakeRect(0,0,pixelsWide,pixelsHigh) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1 respectFlipped:YES hints:nil];
578  [ngc flushGraphics];
579  [NSGraphicsContext setCurrentContext:nil];
580  [NSGraphicsContext restoreGraphicsState];
581 
582  json_object *js = [nbir vuoValue];
583  [nbir release];
584  return js;
585 }
586 @end
587 
595 {
596  // Flip the image data (OpenGL expects flipped data, but NSBitmapImageRep is not flipped).
597  unsigned char *bitmapData = [self bitmapData];
598  unsigned long width = [self size].width;
599  unsigned long height = [self size].height;
600  unsigned long bytesPerRow = width * 4;
601  unsigned char *bitmapDataFlipped = (unsigned char *)malloc(bytesPerRow * height);
602  for (unsigned long y = 0; y < height; ++y)
603  for (unsigned long x = 0; x < width; ++x)
604  {
605  // Swizzle RGBA -> BGRA (so the GPU doesn't have to swizzle during each fetch)
606  bitmapDataFlipped[bytesPerRow * (height - y - 1) + x * 4 + 2] = bitmapData[bytesPerRow * y + x * 4 + 0];
607  bitmapDataFlipped[bytesPerRow * (height - y - 1) + x * 4 + 1] = bitmapData[bytesPerRow * y + x * 4 + 1];
608  bitmapDataFlipped[bytesPerRow * (height - y - 1) + x * 4 + 0] = bitmapData[bytesPerRow * y + x * 4 + 2];
609  bitmapDataFlipped[bytesPerRow * (height - y - 1) + x * 4 + 3] = bitmapData[bytesPerRow * y + x * 4 + 3];
610  }
611 
612  VuoImage vi = VuoImage_makeFromBuffer(bitmapDataFlipped, GL_BGRA, width, height, VuoImageColorDepth_8, ^(void *buffer){ free(buffer); });
613  VuoRetain(vi);
615  VuoRelease(vi);
616  return json;
617 }
618 @end
619 
627 {
628  NSPoint p = [self pointValue];
629  return VuoPoint2d_getJson(VuoPoint2d_make(p.x, p.y));
630 }
631 @end
632 
633 @implementation NSData (VuoRunnerCocoaConversion)
640 {
641  double p[3] = {0,0,0};
642  [self getBytes:p length:sizeof(double)*3];
643  return VuoPoint3d_getJson(VuoPoint3d_make(p[0], p[1], p[2]));
644 }
645 @end
646 
654 {
655  json_object *arrayJson = json_object_new_array();
656  for (id item in self)
657  {
658  json_object *itemJson = [item vuoValue];
659  json_object_array_add(arrayJson, itemJson);
660  }
661  return arrayJson;
662 }
663 @end