Vuo  2.3.1
VuoImage.c
Go to the documentation of this file.
1 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include "type.h"
14 #include "node.h"
15 #include "VuoImage.h"
16 #include "VuoImageRenderer.h"
17 #include "VuoGlContext.h"
18 #include "VuoGlPool.h"
19 
20 #include <CoreFoundation/CoreFoundation.h>
21 
22 #include <IOSurface/IOSurfaceAPI.h>
23 
24 #include <OpenGL/OpenGL.h>
25 #include <OpenGL/CGLMacro.h>
27 void glBindVertexArray(GLuint array);
28 void glDeleteVertexArrays(GLsizei n, const GLuint *arrays);
29 void glGenVertexArrays(GLsizei n, GLuint *arrays);
31 
32 
34 #ifdef VUO_COMPILER
36  "title" : "Image",
37  "description" : "An image residing in GPU memory (GL Texture Object).",
38  "keywords" : [ ],
39  "version" : "1.0.0",
40  "dependencies" : [
41  "VuoBoolean",
42  "VuoColor",
43  "VuoImageColorDepth",
44  "VuoImageWrapMode",
45  "VuoPoint2d",
46  "VuoGlContext",
47  "VuoGlPool",
48  "VuoImageRenderer",
49  "VuoImageBlur",
50  "VuoImageMapColors",
51  "CoreFoundation.framework",
52  "IOSurface.framework"
53  ]
54  });
55 #endif
57 
58 
66 void VuoImage_free(void *texture)
67 {
68  VuoImage t = (VuoImage)texture;
69 // VLog("Freeing image %p %s",t,VuoImage_getSummary(t));
70 
71  // Detach the CPU memory from the GL texture before recycling.
72  if (t->cpuQueueInitialized && json_object_object_length(t->cpuData))
73  {
74  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
75  glBindTexture(t->glTextureTarget, t->glTextureName);
76  GLenum format = GL_BGRA;
77  if (t->glInternalFormat == GL_DEPTH_COMPONENT)
78  format = GL_DEPTH_COMPONENT;
79  GLenum type = VuoGlTexture_getType(format);
80 // VLog("glTexImage2D(%s, 0, %s, %ld, %ld, 0, %s, %s, NULL);", VuoGl_stringForConstant(t->glTextureTarget), VuoGl_stringForConstant(t->glInternalFormat), t->pixelsWide, t->pixelsHigh, VuoGl_stringForConstant(format), VuoGl_stringForConstant(type));
81  glTexImage2D(t->glTextureTarget, 0, t->glInternalFormat, t->pixelsWide, t->pixelsHigh, 0, format, type, NULL);
82  glBindTexture(t->glTextureTarget, 0);
83  });
84  }
85 
86  VuoGlTexture_release(VuoGlTexturePool_Allocate, t->glTextureTarget, t->glInternalFormat, t->pixelsWide, t->pixelsHigh, t->glTextureName);
87 
88  if (t->cpuQueueInitialized)
89  {
90  dispatch_release(t->cpuQueue);
91 
92  json_object_object_foreach(t->cpuData, key, value)
93  {
94 // VLog("%p: freeing %s",t,key);
95  json_object *o;
96  json_object_object_get_ex(value, "buffer", &o);
97  void *buffer = (void *)json_object_get_int64(o);
98 
99  json_object_object_get_ex(value, "freeCallback", &o);
100  void (^freeCallback)(void *) = (void (^)(void *))json_object_get_int64(o);
101 
102  freeCallback(buffer);
103 
104  Block_release(freeCallback);
105  }
106 
107  json_object_put(t->cpuData);
108  }
109 
110  free(t);
111 }
112 
116 static VuoImage VuoImage_make_internal(unsigned int glTextureName, unsigned int glInternalFormat, unsigned long int pixelsWide, unsigned long int pixelsHigh, VuoImage_freeCallback freeCallback, void *freeCallbackContext)
117 {
118  VuoImage t = (VuoImage)malloc(sizeof(struct _VuoImage));
120 
121  t->glTextureName = glTextureName;
122  t->glTextureTarget = GL_TEXTURE_2D;
123  t->glInternalFormat = glInternalFormat;
124  t->pixelsWide = pixelsWide;
125  t->pixelsHigh = pixelsHigh;
126  t->scaleFactor = 1;
127 
128  t->freeCallbackContext = freeCallbackContext;
129 
130  VuoGlTexture_retain(glTextureName, freeCallback, freeCallbackContext);
131 
132  t->cpuQueueInitialized = 0;
133 
134 // VLog("Made image %p %s",t,VuoImage_getSummary(t));
135  return t;
136 }
137 
156 VuoImage VuoImage_make(unsigned int glTextureName, unsigned int glInternalFormat, unsigned long int pixelsWide, unsigned long int pixelsHigh)
157 {
158  if (!glTextureName || !pixelsWide || !pixelsHigh)
159  return NULL;
160 
161  VuoImage t = VuoImage_make_internal(glTextureName, glInternalFormat, pixelsWide, pixelsHigh, NULL, NULL);
162  return t;
163 }
164 
187 VuoImage VuoImage_makeClientOwned(unsigned int glTextureName, unsigned int glInternalFormat, unsigned long int pixelsWide, unsigned long int pixelsHigh, VuoImage_freeCallback freeCallback, void *freeCallbackContext)
188 {
189  if (!glTextureName || !pixelsWide || !pixelsHigh)
190  return NULL;
191 
192  return VuoImage_make_internal(glTextureName, glInternalFormat, pixelsWide, pixelsHigh, freeCallback, freeCallbackContext);
193 }
194 
219 VuoImage VuoImage_makeClientOwnedGlTextureRectangle(unsigned int glTextureName, unsigned int glInternalFormat, unsigned long int pixelsWide, unsigned long int pixelsHigh, VuoImage_freeCallback freeCallback, void *freeCallbackContext)
220 {
221  VuoImage t = VuoImage_makeClientOwned(glTextureName, glInternalFormat, pixelsWide, pixelsHigh, freeCallback, freeCallbackContext);
222  if (!t)
223  return NULL;
224 
225  t->glTextureTarget = GL_TEXTURE_RECTANGLE_ARB;
226 
227  return t;
228 }
229 
253 VuoImage VuoImage_makeFromBuffer(const void *pixels, unsigned int format, unsigned int pixelsWide, unsigned int pixelsHigh, VuoImageColorDepth colorDepth, void (^freeCallback)(void *pixels))
254 {
255  return VuoImage_makeFromBufferWithStride(pixels, format, pixelsWide, pixelsHigh, 0, colorDepth, freeCallback);
256 }
257 
261 VuoImage VuoImage_makeFromBufferWithStride(const void *pixels, unsigned int format, unsigned int pixelsWide, unsigned int pixelsHigh, unsigned int bytesPerRow, VuoImageColorDepth colorDepth, void (^freeCallback)(void *pixels))
262 {
263  if (!pixelsWide || !pixelsHigh)
264  return NULL;
265 
266  __block GLenum internalformat;
267  __block GLuint glTextureName;
268  __block int alignment = 1;
269  __block bool customRowLength = false;
270  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
271 
272  internalformat = VuoImageColorDepth_getGlInternalFormat(format, colorDepth);
273 // VLog("Using format=%s -> internalformat=%s", VuoGl_stringForConstant(format), VuoGl_stringForConstant(internalformat));
274  glTextureName = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_Allocate, GL_TEXTURE_2D, internalformat, pixelsWide, pixelsHigh, format, NULL);
275  if (!glTextureName)
276  return;
277 
278  int bytesPerPixel = VuoGlTexture_getChannelCount(format);
279  GLuint glType;
280  if (colorDepth == VuoImageColorDepth_8)
281  glType = VuoGlTexture_getType(format);
282  else if (colorDepth == VuoImageColorDepth_16)
283  {
284  glType = GL_HALF_FLOAT_ARB;
285  bytesPerPixel *= 2;
286  }
287  else // if (colorDepth == VuoImageColorDepth_32)
288  {
289  glType = GL_FLOAT;
290  bytesPerPixel *= 4;
291  }
292 
293  if (!bytesPerRow
294  || bytesPerRow == bytesPerPixel * pixelsWide)
295  // Tightly-packed.
296  alignment = 1;
297  else
298  {
299  if (bytesPerRow % 4 == 0)
300  alignment = 4;
301  else if (bytesPerRow % 8 == 0)
302  alignment = 8;
303  else if (bytesPerRow % 2 == 0)
304  alignment = 2;
305  else if (bytesPerRow % bytesPerPixel == 0)
306  {
307  GLuint rowPixels = bytesPerRow / bytesPerPixel;
308  glPixelStorei(GL_UNPACK_ROW_LENGTH, rowPixels);
309  customRowLength = true;
310  }
311  else
312  {
313  VUserLog("Not sure how to handle this stride:");
314  VUserLog(" %dx%d",pixelsWide,pixelsHigh);
315  VUserLog(" bytesPerRow = %d",bytesPerRow);
316  VUserLog(" bytesPerPixel = %d",bytesPerPixel);
317  GLint leftoverBytes = bytesPerRow - bytesPerPixel*pixelsWide;
318  VUserLog(" leftoverBytes = %d",leftoverBytes);
319  return;
320  }
321  }
322  if (alignment != 4)
323  glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
324 
325  glBindTexture(GL_TEXTURE_2D, glTextureName);
326  glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
327 // VLog("glTexImage2D(GL_TEXTURE_2D, 0, %s, %d, %d, 0, %s, %s, %p);", VuoGl_stringForConstant(internalformat), pixelsWide, pixelsHigh, VuoGl_stringForConstant(format), VuoGl_stringForConstant(glType), pixels);
328  glTexImage2D(GL_TEXTURE_2D, 0, internalformat, pixelsWide, pixelsHigh, 0, format, glType, (GLvoid *)pixels);
329  glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
330  glBindTexture(GL_TEXTURE_2D, 0);
331 
332  if (alignment != 4)
333  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
334  if (customRowLength)
335  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
336 
337  // Since returning from this function implies that the texture is ready to use,
338  // flush before returning, to ensure that the enqueued commands to create the texture actually get executed
339  // before it gets used on a different context.
340  glFlushRenderAPPLE();
341 // glFinish();
342 
343  });
344  if (!glTextureName)
345  return NULL;
346 
347  VuoImage image = VuoImage_make(glTextureName, internalformat, pixelsWide, pixelsHigh);
348 
349  dispatch_once(&image->cpuQueueInitialized, ^{});
350  image->cpuQueue = dispatch_queue_create("org.vuo.image.cpu", NULL);
351  image->cpuData = json_object_new_object();
352  char *key = VuoGl_stringForConstant(format);
353  if (alignment != 1)
354  {
355  char *keyWithAlignment;
356  asprintf(&keyWithAlignment, "%s alignment=%d", key, alignment);
357  free(key);
358  key = keyWithAlignment;
359  }
360  if (customRowLength)
361  {
362  char *keyWithRowLength;
363  asprintf(&keyWithRowLength, "%s rowLength=%d", key, bytesPerRow);
364  free(key);
365  key = keyWithRowLength;
366  }
367  json_object *cpuEntry = json_object_new_object();
368  json_object_object_add(cpuEntry, "buffer", json_object_new_int64((long long)pixels));
369  json_object_object_add(cpuEntry, "freeCallback", json_object_new_int64((long long)Block_copy(freeCallback)));
370  json_object_object_add(image->cpuData, key, cpuEntry);
371 // VLog("%p: populated %s",image,key);
372  free(key);
373 
374  return image;
375 }
376 
401 const unsigned char *VuoImage_getBuffer(VuoImage image, unsigned int requestedFormat)
402 {
403  if (!image)
404  return NULL;
405 
406  if (image->glInternalFormat == GL_DEPTH_COMPONENT && requestedFormat != GL_DEPTH_COMPONENT16)
407  {
408  VUserLog("Error: Image has format GL_DEPTH_COMPONENT, which must be fetched as GL_DEPTH_COMPONENT16.");
409  return NULL;
410  }
411 
412  dispatch_once(&image->cpuQueueInitialized, ^{
413  image->cpuQueue = dispatch_queue_create("org.vuo.image.cpu", NULL);
414  image->cpuData = json_object_new_object();
415  });
416 
417  __block unsigned char *pixels = NULL;
418  dispatch_sync(image->cpuQueue, ^{
419  char *key = VuoGl_stringForConstant(requestedFormat);
420  struct json_object *value;
421  if (json_object_object_get_ex(image->cpuData, key, &value))
422  {
423  json_object *o;
424  json_object_object_get_ex(value, "buffer", &o);
425  pixels = (unsigned char *)json_object_get_int64(o);
426  }
427  else
428  {
429  unsigned int channels;
430  if (requestedFormat == GL_LUMINANCE
431  || requestedFormat == GL_R16
432  || requestedFormat == GL_DEPTH_COMPONENT16)
433  channels = 1;
434  else if (requestedFormat == GL_LUMINANCE_ALPHA)
435  channels = 2;
436  else if (requestedFormat == GL_RGB
437  || requestedFormat == GL_BGR)
438  channels = 3;
439  else if (requestedFormat == GL_RGBA
440  || requestedFormat == GL_BGRA
441  || requestedFormat == GL_RGBA16I_EXT
442  || requestedFormat == GL_RGBA16F_ARB
443  || requestedFormat == GL_RGBA32F_ARB)
444  channels = 4;
445  else
446  {
447  VUserLog("Error: Unknown format %s.", VuoGl_stringForConstant(requestedFormat));
448  return;
449  }
450 
451  unsigned int bytesPerChannel = 1;
452  GLuint type = GL_UNSIGNED_BYTE;
453  if (requestedFormat == GL_RGBA16I_EXT
454  || requestedFormat == GL_R16
455  || requestedFormat == GL_DEPTH_COMPONENT16)
456  {
457  bytesPerChannel = 2;
458  type = GL_UNSIGNED_SHORT;
459  }
460  else if (requestedFormat == GL_RGBA16F_ARB)
461  {
462  bytesPerChannel = 2;
463  type = GL_HALF_FLOAT_ARB;
464  }
465  else if (requestedFormat == GL_RGBA32F_ARB)
466  {
467  bytesPerChannel = 4;
468  type = GL_FLOAT;
469  }
470 
471  GLuint actualFormat = requestedFormat;
472  if (requestedFormat == GL_RGBA16I_EXT
473  || requestedFormat == GL_RGBA16F_ARB
474  || requestedFormat == GL_RGBA32F_ARB)
475  actualFormat = GL_BGRA;
476  else if (requestedFormat == GL_DEPTH_COMPONENT16)
477  actualFormat = GL_DEPTH_COMPONENT;
478  else if (requestedFormat == GL_R16)
479  actualFormat = GL_RED;
480 
481  size_t pixelBufferSize = image->pixelsWide * image->pixelsHigh * channels * bytesPerChannel;
482  pixels = (unsigned char *)malloc(pixelBufferSize);
483 
484  // In the seal, use zeroes for the alpha channel,
485  // to lessen the chance that we collide with valid image data.
486  const char *tamperEvidentSeal = "Vuo\0Ima\0ge_\0get\0Buf\0fer\0()\0";
487  int tamperEvidentSealLength = strlen(tamperEvidentSeal);
488  if (pixelBufferSize > tamperEvidentSealLength)
489  strlcpy((char *)pixels, tamperEvidentSeal, pixelBufferSize);
490 
491  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
492 
493  // If each row is quad-aligned, OpenGL doesn't add any padding (good).
494  bool openGLAddsPadding = ((image->pixelsWide * channels * bytesPerChannel) % 4 != 0);
495  if (openGLAddsPadding)
496  // Remove the padding, since VuoImage_getBuffer promises tightly-packed buffers.
497  glPixelStorei(GL_PACK_ALIGNMENT, 1);
498 
499  glBindTexture(image->glTextureTarget, image->glTextureName);
500 // VLog("glGetTexImage(%s, 0, %s, %s, …); on texture internalformat %s", VuoGl_stringForConstant(image->glTextureTarget), VuoGl_stringForConstant(actualFormat), VuoGl_stringForConstant(type), VuoGl_stringForConstant(image->glInternalFormat));
501  glGetTexImage(image->glTextureTarget, 0, actualFormat, type, (GLvoid *)pixels);
502  glBindTexture(image->glTextureTarget, 0);
503 
504  if (openGLAddsPadding)
505  // Restore the default.
506  glPixelStorei(GL_PACK_ALIGNMENT, 4);
507 
508  if (pixelBufferSize > tamperEvidentSealLength && strncmp((char *)pixels, tamperEvidentSeal, strlen(tamperEvidentSeal)) == 0)
509  {
510  GLenum error = glGetError();
511  if (error == GL_NO_ERROR)
512  // But as of macOS 10.14.4, calling glGetTexImage on an IOSurface
513  // now fills the buffer with garbage (instead of leaving the buffer unmodified),
514  // and still doesn't return an error, so we no longer have a way to detect this situation.
515  VUserLog("Warning: glGetTexImage() says it was successful, but it didn't actually copy any data. This might happen if the input texture has an IOSurface bound to it.");
516  else
517  VUserLog("OpenGL Error: %d", error);
518  free(pixels);
519  pixels = NULL;
520  return;
521  }
522 
523  });
524  if (!pixels)
525  return;
526 
527  json_object *cpuEntry = json_object_new_object();
528  json_object_object_add(cpuEntry, "buffer", json_object_new_int64((long long)pixels));
529  json_object_object_add(cpuEntry, "freeCallback", json_object_new_int64((long long)Block_copy( ^(void *buffer){ free(buffer); } )));
530  json_object_object_add(image->cpuData, key, cpuEntry);
531 // VLog("%p: populated %s",image,key);
532  }
533  free(key);
534  });
535 
536  return pixels;
537 }
538 
543 {
544  __block GLint wrapModeGL;
545  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
546 
547  glBindTexture(image->glTextureTarget, image->glTextureName);
548 
549  glGetTexParameteriv(image->glTextureTarget, GL_TEXTURE_WRAP_S, &wrapModeGL);
550  // Ignore GL_TEXTURE_WRAP_T since Vuo assumes it's the same as _S.
551 
552  glBindTexture(image->glTextureTarget, 0);
553 
554  });
555 
556  if (wrapModeGL == GL_CLAMP_TO_EDGE)
557  return VuoImageWrapMode_ClampEdge;
558  else if (wrapModeGL == GL_REPEAT)
559  return VuoImageWrapMode_Repeat;
560  else if (wrapModeGL == GL_MIRRORED_REPEAT)
561  return VuoImageWrapMode_MirroredRepeat;
562 
563  return VuoImageWrapMode_None;
564 }
565 
570 {
571  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
572 
573  glBindTexture(image->glTextureTarget, image->glTextureName);
574 
575  GLint wrapModeGL = GL_CLAMP_TO_BORDER;
576  if (wrapMode == VuoImageWrapMode_ClampEdge)
577  wrapModeGL = GL_CLAMP_TO_EDGE;
578  else if (wrapMode == VuoImageWrapMode_Repeat)
579  wrapModeGL = GL_REPEAT;
580  else if (wrapMode == VuoImageWrapMode_MirroredRepeat)
581  wrapModeGL = GL_MIRRORED_REPEAT;
582 
583  glTexParameteri(image->glTextureTarget, GL_TEXTURE_WRAP_S, wrapModeGL);
584  glTexParameteri(image->glTextureTarget, GL_TEXTURE_WRAP_T, wrapModeGL);
585 
586  glBindTexture(image->glTextureTarget, 0);
587 
588  // Ensure the command queue gets executed before we return,
589  // since the VuoShader might immediately be used on another context.
590  glFlushRenderAPPLE();
591 
592  });
593 }
594 
598 VuoImage VuoImage_makeColorImage(VuoColor color, unsigned int pixelsWide, unsigned int pixelsHigh)
599 {
600  if (!pixelsWide || !pixelsHigh)
601  return NULL;
602 
604  VuoRetain(shader);
605  VuoImage image = VuoImageRenderer_render(shader, pixelsWide, pixelsHigh, VuoImageColorDepth_8);
606  VuoRelease(shader);
607  return image;
608 }
609 
621 VuoImage VuoImage_makeCopy(VuoImage image, bool flip, unsigned int forcePixelsWide, unsigned int forcePixelsHigh, bool forceAlpha)
622 {
623  VuoShader shader = NULL;
624  if (image->glTextureTarget == GL_TEXTURE_2D)
625  shader = VuoShader_makeUnlitAlphaPassthruImageShader(image, flip);
626  else if (image->glTextureTarget == GL_TEXTURE_RECTANGLE_ARB)
628  else
629  {
630  VUserLog("Error: Unknown glTextureTarget %s", VuoGl_stringForConstant(image->glTextureTarget));
631  return NULL;
632  }
633  VuoRetain(shader);
634 
635  if (forceAlpha)
636  shader->isTransparent = true;
637 
638  VuoImage img = VuoImageRenderer_render(shader,
639  forcePixelsWide ? forcePixelsWide : image->pixelsWide,
640  forcePixelsHigh ? forcePixelsHigh : image->pixelsHigh,
641  VuoImage_getColorDepth(image));
642 
643  VuoRelease(shader);
644 
645  return img;
646 }
647 
654 {
655  VuoShader frag = VuoShader_makeUnlitImageShader(image, 1);
656  VuoRetain(frag);
657 
658  GLuint textureName = VuoImageRenderer_draw_internal(frag, image->pixelsWide, image->pixelsHigh, VuoImage_getColorDepth(image), false, true, 0, NULL);
659 
660  VuoImage img = VuoImage_make_internal(textureName, image->glInternalFormat, image->pixelsWide, image->pixelsHigh, NULL, NULL);
661  img->glTextureTarget = GL_TEXTURE_RECTANGLE_ARB;
662 
663  VuoRelease(frag);
664 
665  return img;
666 }
667 
671 bool VuoImage_areEqualWithinTolerance(const VuoImage a, const VuoImage b, const unsigned char tolerance)
672 {
673  if (!a && !b)
674  return true;
675  if (!a || !b)
676  return false;
677 
678  if (a->pixelsWide != b->pixelsWide
679  || a->pixelsHigh != b->pixelsHigh)
680  return false;
681 
682  if (a->glTextureName == b->glTextureName)
683  return true;
684 
685  const unsigned char *aPixels = VuoImage_getBuffer(a, GL_BGRA);
686  const unsigned char *bPixels = VuoImage_getBuffer(b, GL_BGRA);
687 
688  unsigned char aChannels = VuoGlTexture_getChannelCount(a->glInternalFormat);
689  unsigned char bChannels = VuoGlTexture_getChannelCount(b->glInternalFormat);
690  if (aChannels == 4 && bChannels == 1)
691  {
692  // Treat 1-channel red images as equal to opaque greyscale BGRA images.
693  for (unsigned int i = 0; i < a->pixelsWide * a->pixelsHigh; ++i)
694  if (abs(aPixels[i*4+0] - bPixels[i*4+2]) > tolerance
695  || abs(aPixels[i*4+1] - bPixels[i*4+2]) > tolerance
696  || abs(aPixels[i*4+2] - bPixels[i*4+2]) > tolerance
697  || abs(aPixels[i*4+3] - bPixels[i*4+3]) > tolerance)
698  {
699  VDebugLog("Difference found at pixel coordinate (%ld,%ld): RGBA %d,%d,%d,%d vs %d,%d,%d,%d",
700  i%a->pixelsWide, i/a->pixelsWide,
701  aPixels[i*4+2],aPixels[i*4+1],aPixels[i*4+0],aPixels[i*4+3],
702  bPixels[i*4+2],bPixels[i*4+2],bPixels[i*4+2],bPixels[i*4+3]);
703  return false;
704  }
705  return true;
706  }
707  else if (aChannels == 1 && bChannels == 4)
708  {
709  // Treat 1-channel red images as equal to opaque greyscale BGRA images.
710  for (unsigned int i = 0; i < a->pixelsWide * a->pixelsHigh; ++i)
711  if (abs(aPixels[i*4+2] - bPixels[i*4+0]) > tolerance
712  || abs(aPixels[i*4+2] - bPixels[i*4+1]) > tolerance
713  || abs(aPixels[i*4+2] - bPixels[i*4+2]) > tolerance
714  || abs(aPixels[i*4+3] - bPixels[i*4+3]) > tolerance)
715  {
716  VDebugLog("Difference found at pixel coordinate (%ld,%ld): RGBA %d,%d,%d,%d vs %d,%d,%d,%d",
717  i%a->pixelsWide, i/a->pixelsWide,
718  aPixels[i*4+2],aPixels[i*4+2],aPixels[i*4+2],aPixels[i*4+3],
719  bPixels[i*4+2],bPixels[i*4+1],bPixels[i*4+0],bPixels[i*4+3]);
720  return false;
721  }
722  return true;
723  }
724 
725  for (unsigned int i = 0; i < a->pixelsWide * a->pixelsHigh * 4; ++i)
726  if (abs(aPixels[i] - bPixels[i]) > tolerance)
727  {
728  unsigned int p = (i/4)*4; // Round down to the start of this 32bit pixel.
729  VDebugLog("Difference found at pixel coordinate (%ld,%ld): abs(%d - %d) > %d (RGBA %d,%d,%d,%d vs %d,%d,%d,%d)",
730  i%a->pixelsWide, i/a->pixelsWide,
731  aPixels[i], bPixels[i], tolerance,
732  aPixels[p+2],aPixels[p+1],aPixels[p+0],aPixels[p+3],
733  bPixels[p+2],bPixels[p+1],bPixels[p+0],bPixels[p+3]);
734  return false;
735  }
736 
737  return true;
738 }
739 
751 bool VuoImage_areEqual(const VuoImage a, const VuoImage b)
752 {
753  return VuoImage_areEqualWithinTolerance(a,b,0);
754 }
755 
761 bool VuoImage_isLessThan(const VuoImage a, const VuoImage b)
762 {
763  // Treat null images as greater than non-null images,
764  // so the more useful non-null images sort to the beginning of the list.
765  if (!a || !b)
766  return a && !b;
767 
768  if (a->pixelsWide < b->pixelsWide) return true;
769  if (b->pixelsWide < a->pixelsWide) return false;
770 
771  if (a->pixelsHigh < b->pixelsHigh) return true;
772  /*if (b->pixelsHigh < a->pixelsHigh)*/ return false;
773 }
774 
780 bool VuoImage_isEmpty(const VuoImage image)
781 {
782  if (!image || image->pixelsWide == 0 || image->pixelsHigh == 0)
783  return true;
784 
785  const unsigned char *pixels = VuoImage_getBuffer(image, GL_BGRA);
786  bool foundSubstantialPixel = false;
787  for (unsigned int p = 3; p < image->pixelsWide * image->pixelsHigh * 4; p += 4)
788  if (pixels[p])
789  {
790  foundSubstantialPixel = true;
791  break;
792  }
793  return !foundSubstantialPixel;
794 }
795 
802 bool VuoImage_isBlackOrTransparent(const VuoImage image, const unsigned char tolerance)
803 {
804  if (!image || image->pixelsWide == 0 || image->pixelsHigh == 0)
805  return false;
806 
807  const unsigned char *pixels = VuoImage_getBuffer(image, GL_LUMINANCE);
808  bool foundNonBlackPixel = false;
809  for (unsigned int p = 0; p < image->pixelsWide * image->pixelsHigh; ++p)
810  if (pixels[p] > tolerance)
811  {
812  foundNonBlackPixel = true;
813  break;
814  }
815  return !foundNonBlackPixel;
816 }
817 
824 {
825  return (image && image->pixelsWide && image->pixelsHigh);
826 }
827 
832 {
833  return VuoRectangle_make(0, 0, 2, 2. * image->pixelsHigh / image->pixelsWide);
834 }
835 
840 {
841  if (!image)
842  return VuoImageColorDepth_8;
843 
844  if (image->glInternalFormat == GL_LUMINANCE8
845  || image->glInternalFormat == GL_LUMINANCE8_ALPHA8
846  || image->glInternalFormat == GL_RGB
847  || image->glInternalFormat == GL_RGBA
848  || image->glInternalFormat == GL_RGBA8
849  || image->glInternalFormat == GL_BGRA
850  || image->glInternalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT
851  || image->glInternalFormat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
852  return VuoImageColorDepth_8;
853  else if (image->glInternalFormat == GL_LUMINANCE16F_ARB
854  || image->glInternalFormat == GL_LUMINANCE_ALPHA16F_ARB
855  || image->glInternalFormat == GL_DEPTH_COMPONENT
856  || image->glInternalFormat == GL_RGB16
857  || image->glInternalFormat == GL_RGBA16
858  || image->glInternalFormat == GL_RGB16F_ARB
859  || image->glInternalFormat == GL_RGBA16F_ARB)
860  return VuoImageColorDepth_16;
861  else if (image->glInternalFormat == GL_LUMINANCE32F_ARB
862  || image->glInternalFormat == GL_LUMINANCE_ALPHA32F_ARB
863  || image->glInternalFormat == GL_RGB32F_ARB
864  || image->glInternalFormat == GL_RGBA32F_ARB)
865  return VuoImageColorDepth_32;
866 
867  char *formatString = VuoGl_stringForConstant(image->glInternalFormat);
868  VUserLog("Error: Unknown glInternalFormat %x (%s)", image->glInternalFormat, formatString);
869  free(formatString);
870  return VuoImageColorDepth_8;
871 }
872 
911 {
912  return VuoImage_makeFromJsonWithDimensions(js, 0, 0);
913 }
914 
919 {
920  VuoGlTexturePool_disuse(VuoGlTexturePool_AllocateIOSurface, i->glTextureTarget, i->glInternalFormat, i->pixelsWide, i->pixelsHigh, i->glTextureName);
921 }
922 
934 VuoImage VuoImage_makeFromJsonWithDimensions(struct json_object *js, unsigned int requestedPixelsWide, unsigned int requestedPixelsHigh)
935 {
936  if (!js)
937  return NULL;
938 
939  {
940  json_object * o;
941  if (json_object_object_get_ex(js, "pointer", &o))
942  {
943  VuoImage im = (VuoImage)json_object_get_int64(o);
944  if ((requestedPixelsWide == 0 && requestedPixelsHigh == 0)
945  || (im->pixelsWide == requestedPixelsWide && im->pixelsHigh == requestedPixelsHigh))
946  return im;
947  else
948  {
949  VuoImage outputImage = VuoImage_make(im->glTextureName, im->glInternalFormat, requestedPixelsWide, requestedPixelsHigh);
950  outputImage->glTextureTarget = im->glTextureTarget;
951  outputImage->scaleFactor = im->scaleFactor;
952  return outputImage;
953  }
954  }
955  }
956 
957  __block unsigned int glInternalFormat = 0;
958  unsigned long int pixelsWide;
959  unsigned long int pixelsHigh;
960  float scaleFactor = 1;
961 
962  {
963  json_object * o;
964  if (json_object_object_get_ex(js, "pixelsWide", &o))
965  pixelsWide = json_object_get_int64(o);
966  else
967  return NULL;
968  }
969  {
970  json_object * o;
971  if (json_object_object_get_ex(js, "pixelsHigh", &o))
972  pixelsHigh = json_object_get_int64(o);
973  else
974  return NULL;
975  }
976  if (pixelsWide == 0 || pixelsHigh == 0)
977  return NULL;
978 
979  {
980  json_object * o;
981  if (json_object_object_get_ex(js, "scaleFactor", &o))
982  scaleFactor = json_object_get_double(o);
983  }
984 
985  {
986  json_object * o;
987  if (json_object_object_get_ex(js, "color", &o))
988  return VuoImage_makeColorImage(VuoColor_makeFromJson(o), pixelsWide, pixelsHigh);
989  }
990 
991  {
992  json_object * o;
993  if (json_object_object_get_ex(js, "ioSurface", &o))
994  {
995  IOSurfaceID surfID = json_object_get_int(o);
996 // VLog("Converting IOSurfaceID %d",surfID);
997 
998  // Read the IOSurface into a GL_TEXTURE_RECTANGLE_ARB (the only texture type IOSurface supports).
999  __block IOSurfaceRef surf = NULL;
1000  __block GLuint textureRect;
1001  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1002  glInternalFormat = GL_RGBA;
1003  surf = IOSurfaceLookup(surfID);
1004  if (!surf)
1005  {
1006  VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1007  return;
1008  }
1009  textureRect = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, glInternalFormat, pixelsWide, pixelsHigh, GL_BGRA, surf);
1010  glFlushRenderAPPLE();
1011  });
1012  if (!surf)
1013  return NULL;
1014 
1015  // Convert the GL_TEXTURE_RECTANGLE_ARB into GL_TEXTURE_2D.
1016  VuoImage image2d;
1017  {
1018  VuoImage imageRect = VuoImage_makeClientOwnedGlTextureRectangle(textureRect, glInternalFormat, pixelsWide, pixelsHigh, VuoImage_IOSurfaceTextureFree, NULL);
1019 
1020  imageRect->glTextureTarget = GL_TEXTURE_RECTANGLE_ARB;
1021  VuoLocal(imageRect);
1022 
1023  VuoShader shader = VuoShader_makeGlTextureRectangleShader(imageRect, 1);
1024  VuoLocal(shader);
1025 
1026  image2d = VuoImageRenderer_render(shader,
1027  requestedPixelsWide ? requestedPixelsWide : pixelsWide,
1028  requestedPixelsHigh ? requestedPixelsHigh : pixelsHigh,
1029  VuoImage_getColorDepth(imageRect));
1030  }
1031 
1033  CFRelease(surf);
1034 
1035  image2d->scaleFactor = scaleFactor;
1036 
1037  return image2d;
1038  }
1039  }
1040 
1041  return NULL;
1042 }
1043 
1064 GLuint VuoImage_resolveInterprocessJsonUsingTextureProvider(struct json_object *js, GLuint (^provider)(unsigned int pixelsWide, unsigned int pixelsHigh), unsigned int *outputPixelsWide, unsigned int *outputPixelsHigh, void *outputIOSurface)
1065 {
1066  json_object *o;
1067 
1068  if (!json_object_object_get_ex(js, "pixelsWide", &o))
1069  return 0;
1070  *outputPixelsWide = json_object_get_int64(o);
1071 
1072  if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1073  return 0;
1074  *outputPixelsHigh = json_object_get_int64(o);
1075 
1076  if (!json_object_object_get_ex(js, "ioSurface", &o))
1077  return 0;
1078  IOSurfaceID surfID = json_object_get_int(o);
1079 
1080  GLuint textureRect = provider(*outputPixelsWide, *outputPixelsHigh);
1081  if (!textureRect)
1082  return 0;
1083 
1084  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1085  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textureRect);
1086 
1087  IOSurfaceRef *surf = outputIOSurface;
1088  *surf = IOSurfaceLookup(surfID);
1089  if (!*surf)
1090  {
1091  VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1092  return;
1093  }
1094 
1095  CGLError err = CGLTexImageIOSurface2D(cgl_ctx, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, (GLsizei)*outputPixelsWide, (GLsizei)*outputPixelsHigh, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, *surf, 0);
1096  if(err != kCGLNoError)
1097  {
1098 #pragma clang diagnostic push
1099 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1100  VUserLog("Error in CGLTexImageIOSurface2D(): %s", CGLErrorString(err));
1101 #pragma clang diagnostic pop
1102  return;
1103  }
1104  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1105  glFlushRenderAPPLE();
1106  });
1107 
1108  return textureRect;
1109 }
1110 
1111 static bool VuoImage_resolveInterprocessJsonOntoFramebufferInternal(IOSurfaceRef surf, VuoGlContext context, GLsizei pixelsWide, GLsizei pixelsHigh, bool flip, bool stretch);
1112 
1134 bool VuoImage_resolveInterprocessJsonUsingClientTexture(struct json_object *js, GLuint clientTextureName, unsigned int pixelsWide, unsigned int pixelsHigh, void *outputIOSurface)
1135 {
1136  if (!clientTextureName)
1137  return false;
1138 
1139  json_object *o;
1140 
1141  if (!json_object_object_get_ex(js, "pixelsWide", &o))
1142  return false;
1143  unsigned long inputPixelsWide = json_object_get_int64(o);
1144 
1145  if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1146  return false;
1147  unsigned long inputPixelsHigh = json_object_get_int64(o);
1148 
1149  if (!json_object_object_get_ex(js, "ioSurface", &o))
1150  return false;
1151  IOSurfaceID surfID = json_object_get_int(o);
1152 
1153  __block bool success = true;
1154  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1155  IOSurfaceRef *surf = outputIOSurface;
1156  *surf = IOSurfaceLookup(surfID);
1157  if (!*surf)
1158  {
1159  VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1160  success = false;
1161  return;
1162  }
1163 
1164  bool shouldResize = (inputPixelsWide != pixelsWide
1165  || inputPixelsHigh != pixelsHigh);
1166  if (shouldResize)
1167  {
1168  VuoShader_resetContext(cgl_ctx);
1169 
1170  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, clientTextureName);
1171  glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, pixelsWide, pixelsHigh, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
1172  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1173 
1174  GLuint outputFramebuffer;
1175  glGenFramebuffers(1, &outputFramebuffer);
1176  glBindFramebuffer(GL_FRAMEBUFFER, outputFramebuffer);
1177  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, clientTextureName, 0);
1178 
1179  glViewport(0, 0, pixelsWide, pixelsHigh);
1180 
1181  success = VuoImage_resolveInterprocessJsonOntoFramebufferInternal(*surf, cgl_ctx, inputPixelsWide, inputPixelsHigh, false, true);
1182 
1183  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
1184  glBindFramebuffer(GL_FRAMEBUFFER, 0);
1185  glDeleteFramebuffers(1, &outputFramebuffer);
1186  }
1187  else
1188  {
1189  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, clientTextureName);
1190 
1191  CGLError err = CGLTexImageIOSurface2D(cgl_ctx, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, (GLsizei)inputPixelsWide, (GLsizei)inputPixelsHigh, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, *surf, 0);
1192  if (err != kCGLNoError)
1193  {
1194 #pragma clang diagnostic push
1195 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1196  VUserLog("Error in CGLTexImageIOSurface2D(): %s", CGLErrorString(err));
1197 #pragma clang diagnostic pop
1198  success = false;
1199  return;
1200  }
1201 
1202  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1203  }
1204 
1205  glFlushRenderAPPLE();
1206  });
1207 
1208  return success;
1209 }
1210 
1227 {
1228  json_object *o;
1229  if (!json_object_object_get_ex(js, "pixelsWide", &o))
1230  return false;
1231  GLsizei pixelsWide = json_object_get_int64(o);
1232 
1233  if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1234  return false;
1235  GLsizei pixelsHigh = json_object_get_int64(o);
1236 
1237  if (!json_object_object_get_ex(js, "ioSurface", &o))
1238  return false;
1239  IOSurfaceID surfID = json_object_get_int(o);
1240  IOSurfaceRef surf = IOSurfaceLookup(surfID);
1241  if (!surf)
1242  {
1243  VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1244  return false;
1245  }
1246 
1247  bool ret = VuoImage_resolveInterprocessJsonOntoFramebufferInternal(surf, context, pixelsWide, pixelsHigh, flip, stretch);
1248 
1250  CFRelease(surf);
1251  return ret;
1252 }
1253 
1257 static GLuint CompileShader(CGLContextObj cgl_ctx, GLenum type, const char *source)
1258 {
1259  GLint length = (GLint)strlen(source);
1260  GLuint shader = glCreateShader(type);
1261  glShaderSource(shader, 1, (const GLchar**)&source, &length);
1262  glCompileShader(shader);
1263 
1264  int infologLength = 0;
1265  glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLength);
1266  if (infologLength > 0)
1267  {
1268  char *infoLog = (char *)malloc(infologLength);
1269  int charsWritten = 0;
1270  glGetShaderInfoLog(shader, infologLength, &charsWritten, infoLog);
1271  VUserLog("%s", infoLog);
1272  free(infoLog);
1273  }
1274  return shader;
1275 }
1276 
1279 
1283 static bool VuoImage_resolveInterprocessJsonOntoFramebufferInternal(IOSurfaceRef surf, VuoGlContext context, GLsizei pixelsWide, GLsizei pixelsHigh, bool flip, bool stretch)
1284 {
1285  CGLContextObj cgl_ctx = (CGLContextObj)context;
1286 
1287  static bool openGL32Core;
1288  static GLuint vertexArray;
1289  static GLuint program;
1290  static GLuint receiveTextureOffsetAndSizeUniform;
1292  openGL32Core = VuoGlContext_isOpenGL32Core((VuoGlContext)cgl_ctx);
1293 
1294  char *vertexShaderSource;
1295  char *fragmentShaderSource;
1296  if (openGL32Core)
1297  {
1298  // The following 2 `gl*VertexArrays` calls use the thread-local context (not CGLMacro).
1299  CGLSetCurrentContext(cgl_ctx);
1300 
1301  glGenVertexArrays(1, &vertexArray);
1302  glBindVertexArray(vertexArray);
1303 
1304  vertexShaderSource = VUOSHADER_GLSL_SOURCE(150,
1305  in vec2 position;
1306  in vec2 textureCoordinate;
1307  out vec2 fragmentTextureCoordinate;
1308  void main()
1309  {
1310  fragmentTextureCoordinate = textureCoordinate;
1311  gl_Position = vec4(position.x, position.y, 0., 1.);
1312  }
1313  );
1314  fragmentShaderSource = VUOSHADER_GLSL_SOURCE(150,
1315  uniform sampler2DRect receiveTexture;
1316  uniform vec4 receiveTextureOffsetAndSize;
1317  in vec2 fragmentTextureCoordinate;
1318  out vec4 FragColor;
1319  void main()
1320  {
1321  FragColor = texture(receiveTexture, receiveTextureOffsetAndSize.xy + fragmentTextureCoordinate * receiveTextureOffsetAndSize.zw);
1322  }
1323  );
1324  }
1325  else
1326  {
1327  // OpenGL 2.1 context.
1328 
1329  glGenVertexArraysAPPLE(1, &vertexArray);
1330  glBindVertexArrayAPPLE(vertexArray);
1331 
1332  vertexShaderSource = VUOSHADER_GLSL_SOURCE(120,
1333  attribute vec2 position;
1334  attribute vec2 textureCoordinate;
1335  varying vec2 fragmentTextureCoordinate;
1336  void main()
1337  {
1338  fragmentTextureCoordinate = textureCoordinate;
1339  gl_Position = vec4(position.x, position.y, 0., 1.);
1340  }
1341  );
1342  fragmentShaderSource = VUOSHADER_GLSL_SOURCE(120,
1343  uniform sampler2DRect receiveTexture;
1344  uniform vec4 receiveTextureOffsetAndSize;
1345  varying vec2 fragmentTextureCoordinate;
1346  void main()
1347  {
1348  gl_FragColor = texture2DRect(receiveTexture, receiveTextureOffsetAndSize.xy + fragmentTextureCoordinate * receiveTextureOffsetAndSize.zw);
1349  }
1350  );
1351  }
1352 
1353 
1354  const GLfloat quadPositionsAndTextureCoordinates[] = {
1355  // X Y U V
1356  -1, -1, 0, 0,
1357  1, -1, 1, 0,
1358  -1, 1, 0, 1,
1359 
1360  1, 1, 1, 1,
1361  -1, 1, 0, 1,
1362  1, -1, 1, 0,
1363  };
1364  GLuint quadPTCBuffer;
1365  glGenBuffers(1, &quadPTCBuffer);
1366  glBindBuffer(GL_ARRAY_BUFFER, quadPTCBuffer);
1367  glBufferData(GL_ARRAY_BUFFER, sizeof(quadPositionsAndTextureCoordinates), quadPositionsAndTextureCoordinates, GL_STATIC_DRAW);
1368  VuoGlPool_logVRAMAllocated(sizeof(quadPositionsAndTextureCoordinates));
1369 
1370  GLuint vertexShader = CompileShader(context, GL_VERTEX_SHADER, vertexShaderSource);
1371  GLuint fragmentShader = CompileShader(context, GL_FRAGMENT_SHADER, fragmentShaderSource);
1372  program = glCreateProgram();
1373  glAttachShader(program, vertexShader);
1374  glAttachShader(program, fragmentShader);
1375  glLinkProgram(program);
1376  GLuint positionAttribute = glGetAttribLocation(program, "position");
1377  GLuint textureCoordinateAttribute = glGetAttribLocation(program, "textureCoordinate");
1378  GLuint receiveTextureUniform = glGetUniformLocation(program, "receiveTexture");
1379  receiveTextureOffsetAndSizeUniform = glGetUniformLocation(program, "receiveTextureOffsetAndSize");
1380 
1381  glUseProgram(program);
1382 
1383  glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, (void*)0);
1384  glEnableVertexAttribArray(positionAttribute);
1385 
1386  glVertexAttribPointer(textureCoordinateAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, (void*)(sizeof(GLfloat)*2));
1387  glEnableVertexAttribArray(textureCoordinateAttribute);
1388 
1389  glUniform1i(receiveTextureUniform, 0);
1390  });
1391 
1392 
1393  if (openGL32Core)
1394  {
1395  CGLSetCurrentContext(cgl_ctx);
1396  glBindVertexArray(vertexArray);
1397  }
1398  else
1399  glBindVertexArrayAPPLE(vertexArray);
1400 
1401  GLuint textureRect = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, pixelsWide, pixelsHigh, GL_BGRA, surf);
1402  if (!textureRect)
1403  {
1404  VUserLog("Error: Couldn't allocate texture.");
1405  VGL();
1406  return false;
1407  }
1408  VuoGlTexture_retain(textureRect, NULL, NULL);
1409 
1410  glActiveTexture(GL_TEXTURE0);
1411  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textureRect);
1412 
1413  glUseProgram(program);
1414 
1415  if (stretch)
1416  glUniform4f(receiveTextureOffsetAndSizeUniform, 0, flip ? pixelsHigh : 0, pixelsWide, pixelsHigh * (flip ? -1 : 1));
1417  else
1418  {
1419  // Center the image in the viewport.
1420  GLint viewport[4];
1421  glGetIntegerv(GL_VIEWPORT, viewport);
1422 // VLog("Resolving %dx%d image onto a %dx%d viewport.", pixelsWide, pixelsHigh, viewport[2], viewport[3]);
1423  glUniform4f(receiveTextureOffsetAndSizeUniform,
1424  ((float)pixelsWide - viewport[2]) / 2,
1425  flip ? viewport[3] : 0,
1426  viewport[2],
1427  viewport[3] * (flip ? -1 : 1));
1428  }
1429 
1430  glDrawArrays(GL_TRIANGLES, 0, 6);
1431 
1432  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1433  VuoGlTexture_release(VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, pixelsWide, pixelsHigh, textureRect);
1434  glUseProgram(0);
1435  if (openGL32Core)
1436  glBindVertexArray(0);
1437  else
1438  glBindVertexArrayAPPLE(0);
1439 
1440  glBindBuffer(GL_ARRAY_BUFFER, 0);
1441 
1442  return true;
1443 }
1444 
1452 {
1453  if (!value)
1454  return NULL;
1455 
1456  json_object *js = json_object_new_object();
1457  json_object_object_add(js, "pointer", json_object_new_int64((int64_t)value));
1458  return js;
1459 }
1460 
1470 {
1471  if (!value)
1472  return NULL;
1473 
1474  __block VuoShader shader = NULL;
1475  __block IOSurfaceID surfID = 0;
1476  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1477 // VLog("Creating an IOSurface from glTextureName %d on target %lu",value->glTextureName,value->glTextureTarget);
1478 
1479  if (value->glTextureTarget == GL_TEXTURE_2D)
1480  shader = VuoShader_makeUnlitAlphaPassthruImageShader(value, false);
1481  else if (value->glTextureTarget == GL_TEXTURE_RECTANGLE_ARB)
1483  VuoRetain(shader);
1484 
1485  surfID = VuoImageRenderer_draw_internal(shader, value->pixelsWide, value->pixelsHigh, VuoImage_getColorDepth(value), true, true, 0, NULL);
1486 // VLog("Created IOSurfaceID %d",surfID);
1487 
1488  // Ensure the command queue gets executed before we return,
1489  // since the IOSurface might immediately be used on another context.
1490  glFlushRenderAPPLE();
1491  });
1492  if (!surfID)
1493  return NULL;
1494 
1495  json_object * js = json_object_new_object();
1496  {
1497  json_object * o = json_object_new_int(surfID);
1498  json_object_object_add(js, "ioSurface", o);
1499  }
1500 
1501  {
1502  json_object * o = json_object_new_int64(value->pixelsWide);
1503  json_object_object_add(js, "pixelsWide", o);
1504  }
1505  {
1506  json_object * o = json_object_new_int64(value->pixelsHigh);
1507  json_object_object_add(js, "pixelsHigh", o);
1508  }
1509  {
1510  json_object *o = json_object_new_double(value->scaleFactor);
1511  json_object_object_add(js, "scaleFactor", o);
1512  }
1513  // VuoShader_makeUnlitImageShader retains the image; VuoRelease(shader) then releases it.
1514  // So don't release the shader until we're done with the image, in case this release is its last.
1515  VuoRelease(shader);
1516 
1517  return js;
1518 }
1519 
1530 char * VuoImage_getSummary(const VuoImage value)
1531 {
1532  if (!value)
1533  return strdup("No image");
1534 
1535  const char *type;
1536  switch (value->glInternalFormat)
1537  {
1538  case GL_RGB: type = "RGB, each channel 8-bit unsigned integer"; break;
1539  case GL_RGB16F_ARB: type = "RGB, each channel 16-bit signed float"; break;
1540  case GL_RGB32F_ARB: type = "RGB, each channel 32-bit signed float"; break;
1541  case GL_RGBA: type = "RGBA, each channel 8-bit unsigned integer"; break;
1542  case GL_RGBA16F_ARB: type = "RGBA, each channel 16-bit signed float"; break;
1543  case GL_RGBA32F_ARB: type = "RGBA, each channel 32-bit signed float"; break;
1544  case GL_LUMINANCE8: type = "intensity, 8-bit unsigned integer"; break;
1545  case GL_LUMINANCE16F_ARB: type = "intensity, 16-bit signed float"; break;
1546  case GL_LUMINANCE32F_ARB: type = "intensity, 32-bit signed float"; break;
1547  case GL_LUMINANCE8_ALPHA8: type = "intensity+alpha, each channel 8-bit unsigned integer"; break;
1548  case GL_LUMINANCE_ALPHA16F_ARB: type = "intensity+alpha, each channel 16-bit signed float"; break;
1549  case GL_LUMINANCE_ALPHA32F_ARB: type = "intensity+alpha, each channel 32-bit signed float"; break;
1550  case GL_DEPTH_COMPONENT: type = "intensity, 16-bit signed float"; break;
1551  default: type = "(unknown)";
1552  }
1553 
1554  char *target = VuoGl_stringForConstant(value->glTextureTarget);
1555  char *internalformat = VuoGl_stringForConstant(value->glInternalFormat);
1556 
1557  char *summary = VuoText_format("<div>%luĂ—%lu pixels @ %gx</div>\n<div>%s</div>\n<div>OpenGL: %s, %s, ID %u</div>",
1558  value->pixelsWide, value->pixelsHigh,
1559  value->scaleFactor,
1560  type,
1561  target,
1562  internalformat,
1563  value->glTextureName);
1564 
1565  free(internalformat);
1566  free(target);
1567 
1568  return summary;
1569 }