Vuo 2.4.4
Loading...
Searching...
No Matches
VuoImage.c
Go to the documentation of this file.
1
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include "VuoImage.h"
14#include "VuoImageRenderer.h"
15#include "VuoGlContext.h"
16#include "VuoGlPool.h"
17
18#include <CoreFoundation/CoreFoundation.h>
19
20#include <IOSurface/IOSurfaceAPI.h>
21
22#include <OpenGL/OpenGL.h>
23#include <OpenGL/CGLMacro.h>
25void glBindVertexArray(GLuint array);
26void glDeleteVertexArrays(GLsizei n, const GLuint *arrays);
27void glGenVertexArrays(GLsizei n, GLuint *arrays);
29
30
32#ifdef VUO_COMPILER
34 "title" : "Image",
35 "description" : "An image residing in GPU memory (GL Texture Object).",
36 "keywords" : [ ],
37 "version" : "1.0.0",
38 "dependencies" : [
39 "VuoBoolean",
40 "VuoColor",
41 "VuoImageColorDepth",
42 "VuoImageWrapMode",
43 "VuoPoint2d",
44 "VuoGlContext",
45 "VuoGlPool",
46 "VuoImageRenderer",
47 "VuoImageBlur",
48 "VuoImageMapColors",
49 "CoreFoundation.framework",
50 "IOSurface.framework"
51 ]
52 });
53#endif
55
56
64void VuoImage_free(void *texture)
65{
66 VuoImage t = (VuoImage)texture;
67// VLog("Freeing image %p %s",t,VuoImage_getSummary(t));
68
69 // Detach the CPU memory from the GL texture before recycling.
70 if (t->cpuQueueInitialized && json_object_object_length(t->cpuData))
71 {
72 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
73 glBindTexture(t->glTextureTarget, t->glTextureName);
74 GLenum format = GL_BGRA;
75 if (t->glInternalFormat == GL_DEPTH_COMPONENT)
76 format = GL_DEPTH_COMPONENT;
77 GLenum type = VuoGlTexture_getType(format);
78// 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));
79 glTexImage2D(t->glTextureTarget, 0, t->glInternalFormat, t->pixelsWide, t->pixelsHigh, 0, format, type, NULL);
80 glBindTexture(t->glTextureTarget, 0);
81 });
82 }
83
84 VuoGlTexture_release(VuoGlTexturePool_Allocate, t->glTextureTarget, t->glInternalFormat, t->pixelsWide, t->pixelsHigh, t->glTextureName);
85
86 if (t->cpuQueueInitialized)
87 {
88 dispatch_release(t->cpuQueue);
89
90 json_object_object_foreach(t->cpuData, key, value)
91 {
92// VLog("%p: freeing %s",t,key);
93 json_object *o;
94 json_object_object_get_ex(value, "buffer", &o);
95 void *buffer = (void *)json_object_get_int64(o);
96
97 json_object_object_get_ex(value, "freeCallback", &o);
98 void (^freeCallback)(void *) = (void (^)(void *))json_object_get_int64(o);
99
100 freeCallback(buffer);
101
102 Block_release(freeCallback);
103 }
104
105 json_object_put(t->cpuData);
106 }
107
108 free(t);
109}
110
114static VuoImage VuoImage_make_internal(unsigned int glTextureName, unsigned int glInternalFormat, unsigned long int pixelsWide, unsigned long int pixelsHigh, VuoImage_freeCallback freeCallback, void *freeCallbackContext)
115{
116 VuoImage t = (VuoImage)malloc(sizeof(struct _VuoImage));
118
119 t->glTextureName = glTextureName;
120 t->glTextureTarget = GL_TEXTURE_2D;
121 t->glInternalFormat = glInternalFormat;
122 t->pixelsWide = pixelsWide;
123 t->pixelsHigh = pixelsHigh;
124 t->scaleFactor = 1;
125
126 t->freeCallbackContext = freeCallbackContext;
127
128 VuoGlTexture_retain(glTextureName, freeCallback, freeCallbackContext);
129
130 t->cpuQueueInitialized = 0;
131
132// VLog("Made image %p %s",t,VuoImage_getSummary(t));
133 return t;
134}
135
154VuoImage VuoImage_make(unsigned int glTextureName, unsigned int glInternalFormat, unsigned long int pixelsWide, unsigned long int pixelsHigh)
155{
156 if (!glTextureName || !pixelsWide || !pixelsHigh)
157 return NULL;
158
159 VuoImage t = VuoImage_make_internal(glTextureName, glInternalFormat, pixelsWide, pixelsHigh, NULL, NULL);
160 return t;
161}
162
185VuoImage VuoImage_makeClientOwned(unsigned int glTextureName, unsigned int glInternalFormat, unsigned long int pixelsWide, unsigned long int pixelsHigh, VuoImage_freeCallback freeCallback, void *freeCallbackContext)
186{
187 if (!glTextureName || !pixelsWide || !pixelsHigh)
188 return NULL;
189
190 return VuoImage_make_internal(glTextureName, glInternalFormat, pixelsWide, pixelsHigh, freeCallback, freeCallbackContext);
191}
192
217VuoImage VuoImage_makeClientOwnedGlTextureRectangle(unsigned int glTextureName, unsigned int glInternalFormat, unsigned long int pixelsWide, unsigned long int pixelsHigh, VuoImage_freeCallback freeCallback, void *freeCallbackContext)
218{
219 VuoImage t = VuoImage_makeClientOwned(glTextureName, glInternalFormat, pixelsWide, pixelsHigh, freeCallback, freeCallbackContext);
220 if (!t)
221 return NULL;
222
223 t->glTextureTarget = GL_TEXTURE_RECTANGLE_ARB;
224
225 return t;
226}
227
251VuoImage VuoImage_makeFromBuffer(const void *pixels, unsigned int format, unsigned int pixelsWide, unsigned int pixelsHigh, VuoImageColorDepth colorDepth, void (^freeCallback)(void *pixels))
252{
253 return VuoImage_makeFromBufferWithStride(pixels, format, pixelsWide, pixelsHigh, 0, colorDepth, freeCallback);
254}
255
259VuoImage VuoImage_makeFromBufferWithStride(const void *pixels, unsigned int format, unsigned int pixelsWide, unsigned int pixelsHigh, unsigned int bytesPerRow, VuoImageColorDepth colorDepth, void (^freeCallback)(void *pixels))
260{
261 if (!pixelsWide || !pixelsHigh)
262 return NULL;
263
264 __block GLenum internalformat;
265 __block GLuint glTextureName;
266 __block int alignment = 1;
267 __block bool customRowLength = false;
268 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
269
270 internalformat = VuoImageColorDepth_getGlInternalFormat(format, colorDepth);
271// VLog("Using format=%s -> internalformat=%s", VuoGl_stringForConstant(format), VuoGl_stringForConstant(internalformat));
272 glTextureName = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_Allocate, GL_TEXTURE_2D, internalformat, pixelsWide, pixelsHigh, format, NULL);
273 if (!glTextureName)
274 return;
275
276 int bytesPerPixel = VuoGlTexture_getChannelCount(format);
277 GLuint glType;
278 if (colorDepth == VuoImageColorDepth_8)
279 glType = VuoGlTexture_getType(format);
280 else if (colorDepth == VuoImageColorDepth_16)
281 {
282 glType = GL_HALF_FLOAT_ARB;
283 bytesPerPixel *= 2;
284 }
285 else // if (colorDepth == VuoImageColorDepth_32)
286 {
287 glType = GL_FLOAT;
288 bytesPerPixel *= 4;
289 }
290
291 if (!bytesPerRow
292 || bytesPerRow == bytesPerPixel * pixelsWide)
293 // Tightly-packed.
294 alignment = 1;
295 else
296 {
297 if (bytesPerRow % 4 == 0)
298 alignment = 4;
299 else if (bytesPerRow % 8 == 0)
300 alignment = 8;
301 else if (bytesPerRow % 2 == 0)
302 alignment = 2;
303 else if (bytesPerRow % bytesPerPixel == 0)
304 {
305 GLuint rowPixels = bytesPerRow / bytesPerPixel;
306 glPixelStorei(GL_UNPACK_ROW_LENGTH, rowPixels);
307 customRowLength = true;
308 }
309 else
310 {
311 VUserLog("Not sure how to handle this stride:");
312 VUserLog(" %dx%d",pixelsWide,pixelsHigh);
313 VUserLog(" bytesPerRow = %d",bytesPerRow);
314 VUserLog(" bytesPerPixel = %d",bytesPerPixel);
315 GLint leftoverBytes = bytesPerRow - bytesPerPixel*pixelsWide;
316 VUserLog(" leftoverBytes = %d",leftoverBytes);
317 return;
318 }
319 }
320 if (alignment != 4)
321 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
322
323 glBindTexture(GL_TEXTURE_2D, glTextureName);
324 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
325// 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);
326 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, pixelsWide, pixelsHigh, 0, format, glType, (GLvoid *)pixels);
327 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
328 glBindTexture(GL_TEXTURE_2D, 0);
329
330 if (alignment != 4)
331 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
332 if (customRowLength)
333 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
334
335 // Since returning from this function implies that the texture is ready to use,
336 // flush before returning, to ensure that the enqueued commands to create the texture actually get executed
337 // before it gets used on a different context.
338 glFlushRenderAPPLE();
339// glFinish();
340
341 });
342 if (!glTextureName)
343 return NULL;
344
345 VuoImage image = VuoImage_make(glTextureName, internalformat, pixelsWide, pixelsHigh);
346
347 dispatch_once(&image->cpuQueueInitialized, ^{});
348 image->cpuQueue = dispatch_queue_create("org.vuo.image.cpu", NULL);
349 image->cpuData = json_object_new_object();
350 char *key = VuoGl_stringForConstant(format);
351 if (alignment != 1)
352 {
353 char *keyWithAlignment;
354 asprintf(&keyWithAlignment, "%s alignment=%d", key, alignment);
355 free(key);
356 key = keyWithAlignment;
357 }
358 if (customRowLength)
359 {
360 char *keyWithRowLength;
361 asprintf(&keyWithRowLength, "%s rowLength=%d", key, bytesPerRow);
362 free(key);
363 key = keyWithRowLength;
364 }
365 json_object *cpuEntry = json_object_new_object();
366 json_object_object_add(cpuEntry, "buffer", json_object_new_int64((long long)pixels));
367 json_object_object_add(cpuEntry, "freeCallback", json_object_new_int64((long long)Block_copy(freeCallback)));
368 json_object_object_add(image->cpuData, key, cpuEntry);
369// VLog("%p: populated %s",image,key);
370 free(key);
371
372 return image;
373}
374
399const unsigned char *VuoImage_getBuffer(VuoImage image, unsigned int requestedFormat)
400{
401 if (!image)
402 return NULL;
403
404 if (image->glInternalFormat == GL_DEPTH_COMPONENT && requestedFormat != GL_DEPTH_COMPONENT16)
405 {
406 VUserLog("Error: Image has format GL_DEPTH_COMPONENT, which must be fetched as GL_DEPTH_COMPONENT16.");
407 return NULL;
408 }
409
410 dispatch_once(&image->cpuQueueInitialized, ^{
411 image->cpuQueue = dispatch_queue_create("org.vuo.image.cpu", NULL);
412 image->cpuData = json_object_new_object();
413 });
414
415 __block unsigned char *pixels = NULL;
416 dispatch_sync(image->cpuQueue, ^{
417 char *key = VuoGl_stringForConstant(requestedFormat);
418 struct json_object *value;
419 if (json_object_object_get_ex(image->cpuData, key, &value))
420 {
421 json_object *o;
422 json_object_object_get_ex(value, "buffer", &o);
423 pixels = (unsigned char *)json_object_get_int64(o);
424 }
425 else
426 {
427 unsigned int channels;
428 if (requestedFormat == GL_LUMINANCE
429 || requestedFormat == GL_R16
430 || requestedFormat == GL_DEPTH_COMPONENT16)
431 channels = 1;
432 else if (requestedFormat == GL_LUMINANCE_ALPHA)
433 channels = 2;
434 else if (requestedFormat == GL_RGB
435 || requestedFormat == GL_BGR)
436 channels = 3;
437 else if (requestedFormat == GL_RGBA
438 || requestedFormat == GL_BGRA
439 || requestedFormat == GL_RGBA16I_EXT
440 || requestedFormat == GL_RGBA16F_ARB
441 || requestedFormat == GL_RGBA32F_ARB)
442 channels = 4;
443 else
444 {
445 VUserLog("Error: Unknown format %s.", VuoGl_stringForConstant(requestedFormat));
446 return;
447 }
448
449 unsigned int bytesPerChannel = 1;
450 GLuint type = GL_UNSIGNED_BYTE;
451 if (requestedFormat == GL_RGBA16I_EXT
452 || requestedFormat == GL_R16
453 || requestedFormat == GL_DEPTH_COMPONENT16)
454 {
455 bytesPerChannel = 2;
456 type = GL_UNSIGNED_SHORT;
457 }
458 else if (requestedFormat == GL_RGBA16F_ARB)
459 {
460 bytesPerChannel = 2;
461 type = GL_HALF_FLOAT_ARB;
462 }
463 else if (requestedFormat == GL_RGBA32F_ARB)
464 {
465 bytesPerChannel = 4;
466 type = GL_FLOAT;
467 }
468
469 GLuint actualFormat = requestedFormat;
470 if (requestedFormat == GL_RGBA16I_EXT
471 || requestedFormat == GL_RGBA16F_ARB
472 || requestedFormat == GL_RGBA32F_ARB)
473 actualFormat = GL_BGRA;
474 else if (requestedFormat == GL_DEPTH_COMPONENT16)
475 actualFormat = GL_DEPTH_COMPONENT;
476 else if (requestedFormat == GL_R16)
477 actualFormat = GL_RED;
478
479 size_t pixelBufferSize = image->pixelsWide * image->pixelsHigh * channels * bytesPerChannel;
480 pixels = (unsigned char *)malloc(pixelBufferSize);
481
482 // In the seal, use zeroes for the alpha channel,
483 // to lessen the chance that we collide with valid image data.
484 const char *tamperEvidentSeal = "Vuo\0Ima\0ge_\0get\0Buf\0fer\0()\0";
485 int tamperEvidentSealLength = strlen(tamperEvidentSeal);
486 if (pixelBufferSize > tamperEvidentSealLength)
487 strlcpy((char *)pixels, tamperEvidentSeal, pixelBufferSize);
488
489 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
490
491 // If each row is quad-aligned, OpenGL doesn't add any padding (good).
492 bool openGLAddsPadding = ((image->pixelsWide * channels * bytesPerChannel) % 4 != 0);
493 if (openGLAddsPadding)
494 // Remove the padding, since VuoImage_getBuffer promises tightly-packed buffers.
495 glPixelStorei(GL_PACK_ALIGNMENT, 1);
496
497 glBindTexture(image->glTextureTarget, image->glTextureName);
498// VLog("glGetTexImage(%s, 0, %s, %s, …); on texture internalformat %s", VuoGl_stringForConstant(image->glTextureTarget), VuoGl_stringForConstant(actualFormat), VuoGl_stringForConstant(type), VuoGl_stringForConstant(image->glInternalFormat));
499 glGetTexImage(image->glTextureTarget, 0, actualFormat, type, (GLvoid *)pixels);
500 glBindTexture(image->glTextureTarget, 0);
501
502 if (openGLAddsPadding)
503 // Restore the default.
504 glPixelStorei(GL_PACK_ALIGNMENT, 4);
505
506 if (pixelBufferSize > tamperEvidentSealLength && strncmp((char *)pixels, tamperEvidentSeal, strlen(tamperEvidentSeal)) == 0)
507 {
508 GLenum error = glGetError();
509 if (error == GL_NO_ERROR)
510 // But as of macOS 10.14.4, calling glGetTexImage on an IOSurface
511 // now fills the buffer with garbage (instead of leaving the buffer unmodified),
512 // and still doesn't return an error, so we no longer have a way to detect this situation.
513 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.");
514 else
515 VUserLog("OpenGL Error: %d", error);
516 free(pixels);
517 pixels = NULL;
518 return;
519 }
520
521 });
522 if (!pixels)
523 return;
524
525 json_object *cpuEntry = json_object_new_object();
526 json_object_object_add(cpuEntry, "buffer", json_object_new_int64((long long)pixels));
527 json_object_object_add(cpuEntry, "freeCallback", json_object_new_int64((long long)Block_copy( ^(void *buffer){ free(buffer); } )));
528 json_object_object_add(image->cpuData, key, cpuEntry);
529// VLog("%p: populated %s",image,key);
530 }
531 free(key);
532 });
533
534 return pixels;
535}
536
541{
542 __block GLint wrapModeGL;
543 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
544
545 glBindTexture(image->glTextureTarget, image->glTextureName);
546
547 glGetTexParameteriv(image->glTextureTarget, GL_TEXTURE_WRAP_S, &wrapModeGL);
548 // Ignore GL_TEXTURE_WRAP_T since Vuo assumes it's the same as _S.
549
550 glBindTexture(image->glTextureTarget, 0);
551
552 });
553
554 if (wrapModeGL == GL_CLAMP_TO_EDGE)
555 return VuoImageWrapMode_ClampEdge;
556 else if (wrapModeGL == GL_REPEAT)
557 return VuoImageWrapMode_Repeat;
558 else if (wrapModeGL == GL_MIRRORED_REPEAT)
559 return VuoImageWrapMode_MirroredRepeat;
560
561 return VuoImageWrapMode_None;
562}
563
568{
569 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
570
571 glBindTexture(image->glTextureTarget, image->glTextureName);
572
573 GLint wrapModeGL = GL_CLAMP_TO_BORDER;
574 if (wrapMode == VuoImageWrapMode_ClampEdge)
575 wrapModeGL = GL_CLAMP_TO_EDGE;
576 else if (wrapMode == VuoImageWrapMode_Repeat)
577 wrapModeGL = GL_REPEAT;
578 else if (wrapMode == VuoImageWrapMode_MirroredRepeat)
579 wrapModeGL = GL_MIRRORED_REPEAT;
580
581 glTexParameteri(image->glTextureTarget, GL_TEXTURE_WRAP_S, wrapModeGL);
582 glTexParameteri(image->glTextureTarget, GL_TEXTURE_WRAP_T, wrapModeGL);
583
584 glBindTexture(image->glTextureTarget, 0);
585
586 // Ensure the command queue gets executed before we return,
587 // since the VuoShader might immediately be used on another context.
588 glFlushRenderAPPLE();
589
590 });
591}
592
597{
598 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
599 glBindTexture(image->glTextureTarget, image->glTextureName);
600
601 GLint samplingModeGL = GL_LINEAR;
602 if (samplingMode == VuoImageSampling_Nearest)
603 samplingModeGL = GL_NEAREST;
604
605 glTexParameteri(image->glTextureTarget, GL_TEXTURE_MIN_FILTER, samplingModeGL);
606 glTexParameteri(image->glTextureTarget, GL_TEXTURE_MAG_FILTER, samplingModeGL);
607
608 glBindTexture(image->glTextureTarget, 0);
609
610 // Ensure the command queue gets executed before we return,
611 // since the VuoShader might immediately be used on another context.
612 glFlushRenderAPPLE();
613 });
614}
615
619VuoImage VuoImage_makeColorImage(VuoColor color, unsigned int pixelsWide, unsigned int pixelsHigh)
620{
621 if (!pixelsWide || !pixelsHigh)
622 return NULL;
623
625 VuoRetain(shader);
626 VuoImage image = VuoImageRenderer_render(shader, pixelsWide, pixelsHigh, VuoImageColorDepth_8);
627 VuoRelease(shader);
628 return image;
629}
630
642VuoImage VuoImage_makeCopy(VuoImage image, bool flip, unsigned int forcePixelsWide, unsigned int forcePixelsHigh, bool forceAlpha)
643{
644 VuoShader shader = NULL;
645 if (image->glTextureTarget == GL_TEXTURE_2D)
647 else if (image->glTextureTarget == GL_TEXTURE_RECTANGLE_ARB)
649 else
650 {
651 VUserLog("Error: Unknown glTextureTarget %s", VuoGl_stringForConstant(image->glTextureTarget));
652 return NULL;
653 }
654 VuoRetain(shader);
655
656 if (forceAlpha)
657 shader->isTransparent = true;
658
660 forcePixelsWide ? forcePixelsWide : image->pixelsWide,
661 forcePixelsHigh ? forcePixelsHigh : image->pixelsHigh,
663
664 VuoRelease(shader);
665
666 return img;
667}
668
675{
677 VuoRetain(frag);
678
679 GLuint textureName = VuoImageRenderer_draw_internal(frag, image->pixelsWide, image->pixelsHigh, VuoImage_getColorDepth(image), false, true, 0, NULL);
680
681 VuoImage img = VuoImage_make_internal(textureName, image->glInternalFormat, image->pixelsWide, image->pixelsHigh, NULL, NULL);
682 img->glTextureTarget = GL_TEXTURE_RECTANGLE_ARB;
683
684 VuoRelease(frag);
685
686 return img;
687}
688
692bool VuoImage_areEqualWithinTolerance(const VuoImage a, const VuoImage b, const unsigned char tolerance)
693{
694 if (!a && !b)
695 return true;
696 if (!a || !b)
697 return false;
698
699 if (a->pixelsWide != b->pixelsWide
700 || a->pixelsHigh != b->pixelsHigh)
701 return false;
702
703 if (a->glTextureName == b->glTextureName)
704 return true;
705
706 const unsigned char *aPixels = VuoImage_getBuffer(a, GL_BGRA);
707 const unsigned char *bPixels = VuoImage_getBuffer(b, GL_BGRA);
708
709 unsigned char aChannels = VuoGlTexture_getChannelCount(a->glInternalFormat);
710 unsigned char bChannels = VuoGlTexture_getChannelCount(b->glInternalFormat);
711 if (aChannels == 4 && bChannels == 1)
712 {
713 // Treat 1-channel red images as equal to opaque greyscale BGRA images.
714 for (unsigned int i = 0; i < a->pixelsWide * a->pixelsHigh; ++i)
715 if (abs(aPixels[i*4+0] - bPixels[i*4+2]) > tolerance
716 || abs(aPixels[i*4+1] - bPixels[i*4+2]) > tolerance
717 || abs(aPixels[i*4+2] - bPixels[i*4+2]) > tolerance
718 || abs(aPixels[i*4+3] - bPixels[i*4+3]) > tolerance)
719 {
720 VDebugLog("Difference found at pixel coordinate (%ld,%ld): RGBA %d,%d,%d,%d vs %d,%d,%d,%d",
721 i%a->pixelsWide, i/a->pixelsWide,
722 aPixels[i*4+2],aPixels[i*4+1],aPixels[i*4+0],aPixels[i*4+3],
723 bPixels[i*4+2],bPixels[i*4+2],bPixels[i*4+2],bPixels[i*4+3]);
724 return false;
725 }
726 return true;
727 }
728 else if (aChannels == 1 && bChannels == 4)
729 {
730 // Treat 1-channel red images as equal to opaque greyscale BGRA images.
731 for (unsigned int i = 0; i < a->pixelsWide * a->pixelsHigh; ++i)
732 if (abs(aPixels[i*4+2] - bPixels[i*4+0]) > tolerance
733 || abs(aPixels[i*4+2] - bPixels[i*4+1]) > tolerance
734 || abs(aPixels[i*4+2] - bPixels[i*4+2]) > tolerance
735 || abs(aPixels[i*4+3] - bPixels[i*4+3]) > tolerance)
736 {
737 VDebugLog("Difference found at pixel coordinate (%ld,%ld): RGBA %d,%d,%d,%d vs %d,%d,%d,%d",
738 i%a->pixelsWide, i/a->pixelsWide,
739 aPixels[i*4+2],aPixels[i*4+2],aPixels[i*4+2],aPixels[i*4+3],
740 bPixels[i*4+2],bPixels[i*4+1],bPixels[i*4+0],bPixels[i*4+3]);
741 return false;
742 }
743 return true;
744 }
745
746 for (unsigned int i = 0; i < a->pixelsWide * a->pixelsHigh * 4; ++i)
747 if (abs(aPixels[i] - bPixels[i]) > tolerance)
748 {
749 unsigned int p = (i/4)*4; // Round down to the start of this 32bit pixel.
750 VDebugLog("Difference found at pixel coordinate (%ld,%ld): abs(%d - %d) > %d (RGBA %d,%d,%d,%d vs %d,%d,%d,%d)",
751 i%a->pixelsWide, i/a->pixelsWide,
752 aPixels[i], bPixels[i], tolerance,
753 aPixels[p+2],aPixels[p+1],aPixels[p+0],aPixels[p+3],
754 bPixels[p+2],bPixels[p+1],bPixels[p+0],bPixels[p+3]);
755 return false;
756 }
757
758 return true;
759}
760
772bool VuoImage_areEqual(const VuoImage a, const VuoImage b)
773{
775}
776
783{
784 // Treat null images as greater than non-null images,
785 // so the more useful non-null images sort to the beginning of the list.
786 if (!a || !b)
787 return a && !b;
788
789 if (a->pixelsWide < b->pixelsWide) return true;
790 if (b->pixelsWide < a->pixelsWide) return false;
791
792 if (a->pixelsHigh < b->pixelsHigh) return true;
793 /*if (b->pixelsHigh < a->pixelsHigh)*/ return false;
794}
795
801bool VuoImage_isEmpty(const VuoImage image)
802{
803 if (!image || image->pixelsWide == 0 || image->pixelsHigh == 0)
804 return true;
805
806 const unsigned char *pixels = VuoImage_getBuffer(image, GL_BGRA);
807 bool foundSubstantialPixel = false;
808 for (unsigned int p = 3; p < image->pixelsWide * image->pixelsHigh * 4; p += 4)
809 if (pixels[p])
810 {
811 foundSubstantialPixel = true;
812 break;
813 }
814 return !foundSubstantialPixel;
815}
816
823bool VuoImage_isBlackOrTransparent(const VuoImage image, const unsigned char tolerance)
824{
825 if (!image || image->pixelsWide == 0 || image->pixelsHigh == 0)
826 return false;
827
828 const unsigned char *pixels = VuoImage_getBuffer(image, GL_LUMINANCE);
829 bool foundNonBlackPixel = false;
830 for (unsigned int p = 0; p < image->pixelsWide * image->pixelsHigh; ++p)
831 if (pixels[p] > tolerance)
832 {
833 foundNonBlackPixel = true;
834 break;
835 }
836 return !foundNonBlackPixel;
837}
838
845{
846 return (image && image->pixelsWide && image->pixelsHigh);
847}
848
853{
854 return VuoRectangle_make(0, 0, 2, 2. * image->pixelsHigh / image->pixelsWide);
855}
856
861{
862 if (!image)
863 return VuoImageColorDepth_8;
864
865 if (image->glInternalFormat == GL_LUMINANCE8
866 || image->glInternalFormat == GL_LUMINANCE8_ALPHA8
867 || image->glInternalFormat == GL_RGB
868 || image->glInternalFormat == GL_RGBA
869 || image->glInternalFormat == GL_RGBA8
870 || image->glInternalFormat == GL_BGRA
871 || image->glInternalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT
872 || image->glInternalFormat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
873 return VuoImageColorDepth_8;
874 else if (image->glInternalFormat == GL_LUMINANCE16F_ARB
875 || image->glInternalFormat == GL_LUMINANCE_ALPHA16F_ARB
876 || image->glInternalFormat == GL_DEPTH_COMPONENT
877 || image->glInternalFormat == GL_RGB16
878 || image->glInternalFormat == GL_RGBA16
879 || image->glInternalFormat == GL_RGB16F_ARB
880 || image->glInternalFormat == GL_RGBA16F_ARB)
881 return VuoImageColorDepth_16;
882 else if (image->glInternalFormat == GL_LUMINANCE32F_ARB
883 || image->glInternalFormat == GL_LUMINANCE_ALPHA32F_ARB
884 || image->glInternalFormat == GL_RGB32F_ARB
885 || image->glInternalFormat == GL_RGBA32F_ARB)
886 return VuoImageColorDepth_32;
887
888 char *formatString = VuoGl_stringForConstant(image->glInternalFormat);
889 VUserLog("Error: Unknown glInternalFormat %x (%s)", image->glInternalFormat, formatString);
890 free(formatString);
891 return VuoImageColorDepth_8;
892}
893
932{
934}
935
940{
941 VuoGlTexturePool_disuse(VuoGlTexturePool_AllocateIOSurface, i->glTextureTarget, i->glInternalFormat, i->pixelsWide, i->pixelsHigh, i->glTextureName);
942}
943
947static void VuoImage_releaseCallback(struct json_object *js, void *image)
948{
949 VuoRelease(image);
950}
951
963VuoImage VuoImage_makeFromJsonWithDimensions(struct json_object *js, unsigned int requestedPixelsWide, unsigned int requestedPixelsHigh)
964{
965 if (!js)
966 return NULL;
967
968 {
969 json_object * o;
970 if (json_object_object_get_ex(js, "pointer", &o))
971 {
972 VuoImage im = (VuoImage)json_object_get_int64(o);
973 if ((requestedPixelsWide == 0 && requestedPixelsHigh == 0)
974 || (im->pixelsWide == requestedPixelsWide && im->pixelsHigh == requestedPixelsHigh))
975 {
976 json_object_set_userdata(js, im, VuoImage_releaseCallback);
977 return im;
978 }
979 else
980 {
981 VuoImage outputImage = VuoImage_make(im->glTextureName, im->glInternalFormat, requestedPixelsWide, requestedPixelsHigh);
982 outputImage->glTextureTarget = im->glTextureTarget;
983 outputImage->scaleFactor = im->scaleFactor;
984 VuoRelease(im);
985 return outputImage;
986 }
987 }
988 }
989
990 __block unsigned int glInternalFormat = 0;
991 unsigned long int pixelsWide;
992 unsigned long int pixelsHigh;
993 float scaleFactor = 1;
994
995 {
996 json_object * o;
997 if (json_object_object_get_ex(js, "pixelsWide", &o))
998 pixelsWide = json_object_get_int64(o);
999 else
1000 return NULL;
1001 }
1002 {
1003 json_object * o;
1004 if (json_object_object_get_ex(js, "pixelsHigh", &o))
1005 pixelsHigh = json_object_get_int64(o);
1006 else
1007 return NULL;
1008 }
1009 if (pixelsWide == 0 || pixelsHigh == 0)
1010 return NULL;
1011
1012 {
1013 json_object * o;
1014 if (json_object_object_get_ex(js, "scaleFactor", &o))
1015 scaleFactor = json_object_get_double(o);
1016 }
1017
1018 {
1019 json_object * o;
1020 if (json_object_object_get_ex(js, "color", &o))
1021 return VuoImage_makeColorImage(VuoColor_makeFromJson(o), pixelsWide, pixelsHigh);
1022 }
1023
1024 {
1025 json_object * o;
1026 if (json_object_object_get_ex(js, "ioSurface", &o))
1027 {
1028 IOSurfaceID surfID = json_object_get_int(o);
1029// VLog("Converting IOSurfaceID %d",surfID);
1030
1031 // Read the IOSurface into a GL_TEXTURE_RECTANGLE_ARB (the only texture type IOSurface supports).
1032 __block IOSurfaceRef surf = NULL;
1033 __block GLuint textureRect;
1034 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1035 glInternalFormat = GL_RGBA;
1036 surf = IOSurfaceLookup(surfID);
1037 if (!surf)
1038 {
1039 VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1040 return;
1041 }
1042 textureRect = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, glInternalFormat, pixelsWide, pixelsHigh, GL_BGRA, surf);
1043 glFlushRenderAPPLE();
1044 });
1045 if (!surf)
1046 return NULL;
1047
1048 // Convert the GL_TEXTURE_RECTANGLE_ARB into GL_TEXTURE_2D.
1049 VuoImage image2d;
1050 {
1051 VuoImage imageRect = VuoImage_makeClientOwnedGlTextureRectangle(textureRect, glInternalFormat, pixelsWide, pixelsHigh, VuoImage_IOSurfaceTextureFree, NULL);
1052
1053 imageRect->glTextureTarget = GL_TEXTURE_RECTANGLE_ARB;
1054 VuoLocal(imageRect);
1055
1057 VuoLocal(shader);
1058
1059 image2d = VuoImageRenderer_render(shader,
1060 requestedPixelsWide ? requestedPixelsWide : pixelsWide,
1061 requestedPixelsHigh ? requestedPixelsHigh : pixelsHigh,
1062 VuoImage_getColorDepth(imageRect));
1063 }
1064
1066 CFRelease(surf);
1067
1068 image2d->scaleFactor = scaleFactor;
1069
1070 return image2d;
1071 }
1072 }
1073
1074 return NULL;
1075}
1076
1097GLuint VuoImage_resolveInterprocessJsonUsingTextureProvider(struct json_object *js, GLuint (^provider)(unsigned int pixelsWide, unsigned int pixelsHigh), unsigned int *outputPixelsWide, unsigned int *outputPixelsHigh, void *outputIOSurface)
1098{
1099 json_object *o;
1100
1101 if (!json_object_object_get_ex(js, "pixelsWide", &o))
1102 return 0;
1103 *outputPixelsWide = json_object_get_int64(o);
1104
1105 if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1106 return 0;
1107 *outputPixelsHigh = json_object_get_int64(o);
1108
1109 if (!json_object_object_get_ex(js, "ioSurface", &o))
1110 return 0;
1111 IOSurfaceID surfID = json_object_get_int(o);
1112
1113 GLuint textureRect = provider(*outputPixelsWide, *outputPixelsHigh);
1114 if (!textureRect)
1115 return 0;
1116
1117 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1118 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textureRect);
1119
1120 IOSurfaceRef *surf = outputIOSurface;
1121 *surf = IOSurfaceLookup(surfID);
1122 if (!*surf)
1123 {
1124 VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1125 return;
1126 }
1127
1128 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);
1129 if(err != kCGLNoError)
1130 {
1131#pragma clang diagnostic push
1132#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1133 VUserLog("Error in CGLTexImageIOSurface2D(): %s", CGLErrorString(err));
1134#pragma clang diagnostic pop
1135 return;
1136 }
1137 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1138 glFlushRenderAPPLE();
1139 });
1140
1141 return textureRect;
1142}
1143
1144static bool VuoImage_resolveInterprocessJsonOntoFramebufferInternal(IOSurfaceRef surf, VuoGlContext context, GLsizei pixelsWide, GLsizei pixelsHigh, bool flip, bool stretch);
1145
1167bool VuoImage_resolveInterprocessJsonUsingClientTexture(struct json_object *js, GLuint clientTextureName, unsigned int pixelsWide, unsigned int pixelsHigh, void *outputIOSurface)
1168{
1169 if (!clientTextureName)
1170 return false;
1171
1172 json_object *o;
1173
1174 if (!json_object_object_get_ex(js, "pixelsWide", &o))
1175 return false;
1176 unsigned long inputPixelsWide = json_object_get_int64(o);
1177
1178 if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1179 return false;
1180 unsigned long inputPixelsHigh = json_object_get_int64(o);
1181
1182 if (!json_object_object_get_ex(js, "ioSurface", &o))
1183 return false;
1184 IOSurfaceID surfID = json_object_get_int(o);
1185
1186 __block bool success = true;
1187 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1188 IOSurfaceRef *surf = outputIOSurface;
1189 *surf = IOSurfaceLookup(surfID);
1190 if (!*surf)
1191 {
1192 VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1193 success = false;
1194 return;
1195 }
1196
1197 bool shouldResize = (inputPixelsWide != pixelsWide
1198 || inputPixelsHigh != pixelsHigh);
1199 if (shouldResize)
1200 {
1201 VuoShader_resetContext(cgl_ctx);
1202
1203 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, clientTextureName);
1204 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, pixelsWide, pixelsHigh, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
1205 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1206
1207 GLuint outputFramebuffer;
1208 glGenFramebuffers(1, &outputFramebuffer);
1209 glBindFramebuffer(GL_FRAMEBUFFER, outputFramebuffer);
1210 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, clientTextureName, 0);
1211
1212 glViewport(0, 0, pixelsWide, pixelsHigh);
1213
1214 success = VuoImage_resolveInterprocessJsonOntoFramebufferInternal(*surf, cgl_ctx, inputPixelsWide, inputPixelsHigh, false, true);
1215
1216 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
1217 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1218 glDeleteFramebuffers(1, &outputFramebuffer);
1219 }
1220 else
1221 {
1222 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, clientTextureName);
1223
1224 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);
1225 if (err != kCGLNoError)
1226 {
1227#pragma clang diagnostic push
1228#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1229 VUserLog("Error in CGLTexImageIOSurface2D(): %s", CGLErrorString(err));
1230#pragma clang diagnostic pop
1231 success = false;
1232 return;
1233 }
1234
1235 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1236 }
1237
1238 glFlushRenderAPPLE();
1239 });
1240
1241 return success;
1242}
1243
1259bool VuoImage_resolveInterprocessJsonOntoFramebuffer(json_object *js, VuoGlContext context, bool flip, bool stretch)
1260{
1261 json_object *o;
1262 if (!json_object_object_get_ex(js, "pixelsWide", &o))
1263 return false;
1264 GLsizei pixelsWide = json_object_get_int64(o);
1265
1266 if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1267 return false;
1268 GLsizei pixelsHigh = json_object_get_int64(o);
1269
1270 if (!json_object_object_get_ex(js, "ioSurface", &o))
1271 return false;
1272 IOSurfaceID surfID = json_object_get_int(o);
1273 IOSurfaceRef surf = IOSurfaceLookup(surfID);
1274 if (!surf)
1275 {
1276 VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1277 return false;
1278 }
1279
1280 VuoShader_resetContext(context);
1281 bool ret = VuoImage_resolveInterprocessJsonOntoFramebufferInternal(surf, context, pixelsWide, pixelsHigh, flip, stretch);
1282
1284 CFRelease(surf);
1285 return ret;
1286}
1287
1291static GLuint CompileShader(CGLContextObj cgl_ctx, GLenum type, const char *source)
1292{
1293 GLint length = (GLint)strlen(source);
1294 GLuint shader = glCreateShader(type);
1295 glShaderSource(shader, 1, (const GLchar**)&source, &length);
1296 glCompileShader(shader);
1297
1298 int infologLength = 0;
1299 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLength);
1300 if (infologLength > 0)
1301 {
1302 char *infoLog = (char *)malloc(infologLength);
1303 int charsWritten = 0;
1304 glGetShaderInfoLog(shader, infologLength, &charsWritten, infoLog);
1305 VUserLog("%s", infoLog);
1306 free(infoLog);
1307 }
1308 return shader;
1309}
1310
1313
1317static bool VuoImage_resolveInterprocessJsonOntoFramebufferInternal(IOSurfaceRef surf, VuoGlContext context, GLsizei pixelsWide, GLsizei pixelsHigh, bool flip, bool stretch)
1318{
1319 CGLContextObj cgl_ctx = (CGLContextObj)context;
1320
1321 static bool openGL32Core;
1322 static GLuint vertexArray;
1323 static GLuint program;
1324 static GLuint receiveTextureOffsetAndSizeUniform;
1326 openGL32Core = VuoGlContext_isOpenGL32Core((VuoGlContext)cgl_ctx);
1327
1328 char *vertexShaderSource;
1329 char *fragmentShaderSource;
1330 if (openGL32Core)
1331 {
1332 // The following 2 `gl*VertexArrays` calls use the thread-local context (not CGLMacro).
1333 CGLSetCurrentContext(cgl_ctx);
1334
1335 glGenVertexArrays(1, &vertexArray);
1336 glBindVertexArray(vertexArray);
1337
1338 vertexShaderSource = VUOSHADER_GLSL_SOURCE(150,
1339 in vec2 position;
1340 in vec2 textureCoordinate;
1341 out vec2 fragmentTextureCoordinate;
1342 void main()
1343 {
1344 fragmentTextureCoordinate = textureCoordinate;
1345 gl_Position = vec4(position.x, position.y, 0., 1.);
1346 }
1347 );
1348 fragmentShaderSource = VUOSHADER_GLSL_SOURCE(150,
1349 uniform sampler2DRect receiveTexture;
1350 uniform vec4 receiveTextureOffsetAndSize;
1351 in vec2 fragmentTextureCoordinate;
1352 out vec4 FragColor;
1353 void main()
1354 {
1355 FragColor = texture(receiveTexture, receiveTextureOffsetAndSize.xy + fragmentTextureCoordinate * receiveTextureOffsetAndSize.zw);
1356 }
1357 );
1358 }
1359 else
1360 {
1361 // OpenGL 2.1 context.
1362
1363 glGenVertexArraysAPPLE(1, &vertexArray);
1364 glBindVertexArrayAPPLE(vertexArray);
1365
1366 vertexShaderSource = VUOSHADER_GLSL_SOURCE(120,
1367 attribute vec2 position;
1368 attribute vec2 textureCoordinate;
1369 varying vec2 fragmentTextureCoordinate;
1370 void main()
1371 {
1372 fragmentTextureCoordinate = textureCoordinate;
1373 gl_Position = vec4(position.x, position.y, 0., 1.);
1374 }
1375 );
1376 fragmentShaderSource = VUOSHADER_GLSL_SOURCE(120,
1377 uniform sampler2DRect receiveTexture;
1378 uniform vec4 receiveTextureOffsetAndSize;
1379 varying vec2 fragmentTextureCoordinate;
1380 void main()
1381 {
1382 gl_FragColor = texture2DRect(receiveTexture, receiveTextureOffsetAndSize.xy + fragmentTextureCoordinate * receiveTextureOffsetAndSize.zw);
1383 }
1384 );
1385 }
1386
1387
1388 const GLfloat quadPositionsAndTextureCoordinates[] = {
1389 // X Y U V
1390 -1, -1, 0, 0,
1391 1, -1, 1, 0,
1392 -1, 1, 0, 1,
1393
1394 1, 1, 1, 1,
1395 -1, 1, 0, 1,
1396 1, -1, 1, 0,
1397 };
1398 GLuint quadPTCBuffer;
1399 glGenBuffers(1, &quadPTCBuffer);
1400 glBindBuffer(GL_ARRAY_BUFFER, quadPTCBuffer);
1401 glBufferData(GL_ARRAY_BUFFER, sizeof(quadPositionsAndTextureCoordinates), quadPositionsAndTextureCoordinates, GL_STATIC_DRAW);
1402 VuoGlPool_logVRAMAllocated(sizeof(quadPositionsAndTextureCoordinates));
1403
1404 GLuint vertexShader = CompileShader(context, GL_VERTEX_SHADER, vertexShaderSource);
1405 GLuint fragmentShader = CompileShader(context, GL_FRAGMENT_SHADER, fragmentShaderSource);
1406 program = glCreateProgram();
1407 glAttachShader(program, vertexShader);
1408 glAttachShader(program, fragmentShader);
1409 glLinkProgram(program);
1410 GLuint positionAttribute = glGetAttribLocation(program, "position");
1411 GLuint textureCoordinateAttribute = glGetAttribLocation(program, "textureCoordinate");
1412 GLuint receiveTextureUniform = glGetUniformLocation(program, "receiveTexture");
1413 receiveTextureOffsetAndSizeUniform = glGetUniformLocation(program, "receiveTextureOffsetAndSize");
1414
1415 glUseProgram(program);
1416
1417 glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, (void*)0);
1418 glEnableVertexAttribArray(positionAttribute);
1419
1420 glVertexAttribPointer(textureCoordinateAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, (void*)(sizeof(GLfloat)*2));
1421 glEnableVertexAttribArray(textureCoordinateAttribute);
1422
1423 glUniform1i(receiveTextureUniform, 0);
1424 });
1425
1426
1427 if (openGL32Core)
1428 {
1429 CGLSetCurrentContext(cgl_ctx);
1430 glBindVertexArray(vertexArray);
1431 }
1432 else
1433 glBindVertexArrayAPPLE(vertexArray);
1434
1435 GLuint textureRect = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, pixelsWide, pixelsHigh, GL_BGRA, surf);
1436 if (!textureRect)
1437 {
1438 VUserLog("Error: Couldn't allocate texture.");
1439 VGL();
1440 return false;
1441 }
1442 VuoGlTexture_retain(textureRect, NULL, NULL);
1443
1444 glActiveTexture(GL_TEXTURE0);
1445 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textureRect);
1446
1447 glUseProgram(program);
1448
1449 if (stretch)
1450 glUniform4f(receiveTextureOffsetAndSizeUniform, 0, flip ? pixelsHigh : 0, pixelsWide, pixelsHigh * (flip ? -1 : 1));
1451 else
1452 {
1453 // Center the image in the viewport.
1454 GLint viewport[4];
1455 glGetIntegerv(GL_VIEWPORT, viewport);
1456// VLog("Resolving %dx%d image onto a %dx%d viewport.", pixelsWide, pixelsHigh, viewport[2], viewport[3]);
1457 glUniform4f(receiveTextureOffsetAndSizeUniform,
1458 ((float)pixelsWide - viewport[2]) / 2,
1459 flip ? viewport[3] : 0,
1460 viewport[2],
1461 viewport[3] * (flip ? -1 : 1));
1462 }
1463
1464 glDrawArrays(GL_TRIANGLES, 0, 6);
1465
1466 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1467 VuoGlTexture_release(VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, pixelsWide, pixelsHigh, textureRect);
1468 glUseProgram(0);
1469 if (openGL32Core)
1471 else
1472 glBindVertexArrayAPPLE(0);
1473
1474 glBindBuffer(GL_ARRAY_BUFFER, 0);
1475
1476 return true;
1477}
1478
1485json_object * VuoImage_getJson(const VuoImage value)
1486{
1487 if (!value)
1488 return NULL;
1489
1490 VuoRetain(value);
1491
1492 json_object *js = json_object_new_object();
1493 json_object_object_add(js, "pointer", json_object_new_int64((int64_t)value));
1494 return js;
1495}
1496
1505json_object * VuoImage_getInterprocessJson(const VuoImage value)
1506{
1507 if (!value)
1508 return NULL;
1509
1510 __block VuoShader shader = NULL;
1511 __block IOSurfaceID surfID = 0;
1512 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1513// VLog("Creating an IOSurface from glTextureName %d on target %lu",value->glTextureName,value->glTextureTarget);
1514
1515 if (value->glTextureTarget == GL_TEXTURE_2D)
1516 shader = VuoShader_makeUnlitAlphaPassthruImageShader(value, false);
1517 else if (value->glTextureTarget == GL_TEXTURE_RECTANGLE_ARB)
1519 VuoRetain(shader);
1520
1521 surfID = VuoImageRenderer_draw_internal(shader, value->pixelsWide, value->pixelsHigh, VuoImage_getColorDepth(value), true, true, 0, NULL);
1522// VLog("Created IOSurfaceID %d",surfID);
1523
1524 // Ensure the command queue gets executed before we return,
1525 // since the IOSurface might immediately be used on another context.
1526 glFlushRenderAPPLE();
1527 });
1528 if (!surfID)
1529 return NULL;
1530
1531 json_object * js = json_object_new_object();
1532 {
1533 json_object * o = json_object_new_int(surfID);
1534 json_object_object_add(js, "ioSurface", o);
1535 }
1536
1537 {
1538 json_object * o = json_object_new_int64(value->pixelsWide);
1539 json_object_object_add(js, "pixelsWide", o);
1540 }
1541 {
1542 json_object * o = json_object_new_int64(value->pixelsHigh);
1543 json_object_object_add(js, "pixelsHigh", o);
1544 }
1545 {
1546 json_object *o = json_object_new_double(value->scaleFactor);
1547 json_object_object_add(js, "scaleFactor", o);
1548 }
1549 // VuoShader_makeUnlitImageShader retains the image; VuoRelease(shader) then releases it.
1550 // So don't release the shader until we're done with the image, in case this release is its last.
1551 VuoRelease(shader);
1552
1553 return js;
1554}
1555
1567{
1568 if (!value)
1569 return strdup("No image");
1570
1571 const char *type;
1572 switch (value->glInternalFormat)
1573 {
1574 case GL_RGB: type = "RGB, each channel 8-bit unsigned integer"; break;
1575 case GL_RGB16F_ARB: type = "RGB, each channel 16-bit signed float"; break;
1576 case GL_RGB32F_ARB: type = "RGB, each channel 32-bit signed float"; break;
1577 case GL_RGBA: type = "RGBA, each channel 8-bit unsigned integer"; break;
1578 case GL_RGBA16F_ARB: type = "RGBA, each channel 16-bit signed float"; break;
1579 case GL_RGBA32F_ARB: type = "RGBA, each channel 32-bit signed float"; break;
1580 case GL_LUMINANCE8: type = "intensity, 8-bit unsigned integer"; break;
1581 case GL_LUMINANCE16F_ARB: type = "intensity, 16-bit signed float"; break;
1582 case GL_LUMINANCE32F_ARB: type = "intensity, 32-bit signed float"; break;
1583 case GL_LUMINANCE8_ALPHA8: type = "intensity+alpha, each channel 8-bit unsigned integer"; break;
1584 case GL_LUMINANCE_ALPHA16F_ARB: type = "intensity+alpha, each channel 16-bit signed float"; break;
1585 case GL_LUMINANCE_ALPHA32F_ARB: type = "intensity+alpha, each channel 32-bit signed float"; break;
1586 case GL_DEPTH_COMPONENT: type = "intensity, 16-bit signed float"; break;
1587 default: type = "(unknown)";
1588 }
1589
1590 char *target = VuoGl_stringForConstant(value->glTextureTarget);
1591 char *internalformat = VuoGl_stringForConstant(value->glInternalFormat);
1592
1593 char *summary = VuoText_format("<div>%luĂ—%lu pixels @ %gx</div>\n<div>%s</div>\n<div>OpenGL: %s, %s, ID %u</div>",
1594 value->pixelsWide, value->pixelsHigh,
1595 value->scaleFactor,
1596 type,
1597 target,
1598 internalformat,
1599 value->glTextureName);
1600
1601 free(internalformat);
1602 free(target);
1603
1604 return summary;
1605}