Vuo  2.4.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>
27void glBindVertexArray(GLuint array);
28void glDeleteVertexArrays(GLsizei n, const GLuint *arrays);
29void 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
66void 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
116static 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
156VuoImage 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
187VuoImage 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
219VuoImage 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
253VuoImage 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
261VuoImage 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
401const 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
598VuoImage 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
621VuoImage 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)
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
639 forcePixelsWide ? forcePixelsWide : image->pixelsWide,
640 forcePixelsHigh ? forcePixelsHigh : image->pixelsHigh,
642
643 VuoRelease(shader);
644
645 return img;
646}
647
654{
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
671bool 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
751bool VuoImage_areEqual(const VuoImage a, const VuoImage b)
752{
754}
755
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
780bool 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
802bool 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{
913}
914
919{
920 VuoGlTexturePool_disuse(VuoGlTexturePool_AllocateIOSurface, i->glTextureTarget, i->glInternalFormat, i->pixelsWide, i->pixelsHigh, i->glTextureName);
921}
922
926static void VuoImage_releaseCallback(struct json_object *js, void *image)
927{
928 VuoRelease(image);
929}
930
942VuoImage VuoImage_makeFromJsonWithDimensions(struct json_object *js, unsigned int requestedPixelsWide, unsigned int requestedPixelsHigh)
943{
944 if (!js)
945 return NULL;
946
947 {
948 json_object * o;
949 if (json_object_object_get_ex(js, "pointer", &o))
950 {
951 VuoImage im = (VuoImage)json_object_get_int64(o);
952 if ((requestedPixelsWide == 0 && requestedPixelsHigh == 0)
953 || (im->pixelsWide == requestedPixelsWide && im->pixelsHigh == requestedPixelsHigh))
954 {
955 json_object_set_userdata(js, im, VuoImage_releaseCallback);
956 return im;
957 }
958 else
959 {
960 VuoImage outputImage = VuoImage_make(im->glTextureName, im->glInternalFormat, requestedPixelsWide, requestedPixelsHigh);
961 outputImage->glTextureTarget = im->glTextureTarget;
962 outputImage->scaleFactor = im->scaleFactor;
963 VuoRelease(im);
964 return outputImage;
965 }
966 }
967 }
968
969 __block unsigned int glInternalFormat = 0;
970 unsigned long int pixelsWide;
971 unsigned long int pixelsHigh;
972 float scaleFactor = 1;
973
974 {
975 json_object * o;
976 if (json_object_object_get_ex(js, "pixelsWide", &o))
977 pixelsWide = json_object_get_int64(o);
978 else
979 return NULL;
980 }
981 {
982 json_object * o;
983 if (json_object_object_get_ex(js, "pixelsHigh", &o))
984 pixelsHigh = json_object_get_int64(o);
985 else
986 return NULL;
987 }
988 if (pixelsWide == 0 || pixelsHigh == 0)
989 return NULL;
990
991 {
992 json_object * o;
993 if (json_object_object_get_ex(js, "scaleFactor", &o))
994 scaleFactor = json_object_get_double(o);
995 }
996
997 {
998 json_object * o;
999 if (json_object_object_get_ex(js, "color", &o))
1000 return VuoImage_makeColorImage(VuoColor_makeFromJson(o), pixelsWide, pixelsHigh);
1001 }
1002
1003 {
1004 json_object * o;
1005 if (json_object_object_get_ex(js, "ioSurface", &o))
1006 {
1007 IOSurfaceID surfID = json_object_get_int(o);
1008// VLog("Converting IOSurfaceID %d",surfID);
1009
1010 // Read the IOSurface into a GL_TEXTURE_RECTANGLE_ARB (the only texture type IOSurface supports).
1011 __block IOSurfaceRef surf = NULL;
1012 __block GLuint textureRect;
1013 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1014 glInternalFormat = GL_RGBA;
1015 surf = IOSurfaceLookup(surfID);
1016 if (!surf)
1017 {
1018 VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1019 return;
1020 }
1021 textureRect = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, glInternalFormat, pixelsWide, pixelsHigh, GL_BGRA, surf);
1022 glFlushRenderAPPLE();
1023 });
1024 if (!surf)
1025 return NULL;
1026
1027 // Convert the GL_TEXTURE_RECTANGLE_ARB into GL_TEXTURE_2D.
1028 VuoImage image2d;
1029 {
1030 VuoImage imageRect = VuoImage_makeClientOwnedGlTextureRectangle(textureRect, glInternalFormat, pixelsWide, pixelsHigh, VuoImage_IOSurfaceTextureFree, NULL);
1031
1032 imageRect->glTextureTarget = GL_TEXTURE_RECTANGLE_ARB;
1033 VuoLocal(imageRect);
1034
1036 VuoLocal(shader);
1037
1038 image2d = VuoImageRenderer_render(shader,
1039 requestedPixelsWide ? requestedPixelsWide : pixelsWide,
1040 requestedPixelsHigh ? requestedPixelsHigh : pixelsHigh,
1041 VuoImage_getColorDepth(imageRect));
1042 }
1043
1045 CFRelease(surf);
1046
1047 image2d->scaleFactor = scaleFactor;
1048
1049 return image2d;
1050 }
1051 }
1052
1053 return NULL;
1054}
1055
1076GLuint VuoImage_resolveInterprocessJsonUsingTextureProvider(struct json_object *js, GLuint (^provider)(unsigned int pixelsWide, unsigned int pixelsHigh), unsigned int *outputPixelsWide, unsigned int *outputPixelsHigh, void *outputIOSurface)
1077{
1078 json_object *o;
1079
1080 if (!json_object_object_get_ex(js, "pixelsWide", &o))
1081 return 0;
1082 *outputPixelsWide = json_object_get_int64(o);
1083
1084 if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1085 return 0;
1086 *outputPixelsHigh = json_object_get_int64(o);
1087
1088 if (!json_object_object_get_ex(js, "ioSurface", &o))
1089 return 0;
1090 IOSurfaceID surfID = json_object_get_int(o);
1091
1092 GLuint textureRect = provider(*outputPixelsWide, *outputPixelsHigh);
1093 if (!textureRect)
1094 return 0;
1095
1096 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1097 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textureRect);
1098
1099 IOSurfaceRef *surf = outputIOSurface;
1100 *surf = IOSurfaceLookup(surfID);
1101 if (!*surf)
1102 {
1103 VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1104 return;
1105 }
1106
1107 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);
1108 if(err != kCGLNoError)
1109 {
1110#pragma clang diagnostic push
1111#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1112 VUserLog("Error in CGLTexImageIOSurface2D(): %s", CGLErrorString(err));
1113#pragma clang diagnostic pop
1114 return;
1115 }
1116 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1117 glFlushRenderAPPLE();
1118 });
1119
1120 return textureRect;
1121}
1122
1123static bool VuoImage_resolveInterprocessJsonOntoFramebufferInternal(IOSurfaceRef surf, VuoGlContext context, GLsizei pixelsWide, GLsizei pixelsHigh, bool flip, bool stretch);
1124
1146bool VuoImage_resolveInterprocessJsonUsingClientTexture(struct json_object *js, GLuint clientTextureName, unsigned int pixelsWide, unsigned int pixelsHigh, void *outputIOSurface)
1147{
1148 if (!clientTextureName)
1149 return false;
1150
1151 json_object *o;
1152
1153 if (!json_object_object_get_ex(js, "pixelsWide", &o))
1154 return false;
1155 unsigned long inputPixelsWide = json_object_get_int64(o);
1156
1157 if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1158 return false;
1159 unsigned long inputPixelsHigh = json_object_get_int64(o);
1160
1161 if (!json_object_object_get_ex(js, "ioSurface", &o))
1162 return false;
1163 IOSurfaceID surfID = json_object_get_int(o);
1164
1165 __block bool success = true;
1166 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1167 IOSurfaceRef *surf = outputIOSurface;
1168 *surf = IOSurfaceLookup(surfID);
1169 if (!*surf)
1170 {
1171 VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1172 success = false;
1173 return;
1174 }
1175
1176 bool shouldResize = (inputPixelsWide != pixelsWide
1177 || inputPixelsHigh != pixelsHigh);
1178 if (shouldResize)
1179 {
1180 VuoShader_resetContext(cgl_ctx);
1181
1182 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, clientTextureName);
1183 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, pixelsWide, pixelsHigh, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
1184 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1185
1186 GLuint outputFramebuffer;
1187 glGenFramebuffers(1, &outputFramebuffer);
1188 glBindFramebuffer(GL_FRAMEBUFFER, outputFramebuffer);
1189 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, clientTextureName, 0);
1190
1191 glViewport(0, 0, pixelsWide, pixelsHigh);
1192
1193 success = VuoImage_resolveInterprocessJsonOntoFramebufferInternal(*surf, cgl_ctx, inputPixelsWide, inputPixelsHigh, false, true);
1194
1195 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
1196 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1197 glDeleteFramebuffers(1, &outputFramebuffer);
1198 }
1199 else
1200 {
1201 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, clientTextureName);
1202
1203 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);
1204 if (err != kCGLNoError)
1205 {
1206#pragma clang diagnostic push
1207#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1208 VUserLog("Error in CGLTexImageIOSurface2D(): %s", CGLErrorString(err));
1209#pragma clang diagnostic pop
1210 success = false;
1211 return;
1212 }
1213
1214 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1215 }
1216
1217 glFlushRenderAPPLE();
1218 });
1219
1220 return success;
1221}
1222
1239{
1240 json_object *o;
1241 if (!json_object_object_get_ex(js, "pixelsWide", &o))
1242 return false;
1243 GLsizei pixelsWide = json_object_get_int64(o);
1244
1245 if (!json_object_object_get_ex(js, "pixelsHigh", &o))
1246 return false;
1247 GLsizei pixelsHigh = json_object_get_int64(o);
1248
1249 if (!json_object_object_get_ex(js, "ioSurface", &o))
1250 return false;
1251 IOSurfaceID surfID = json_object_get_int(o);
1252 IOSurfaceRef surf = IOSurfaceLookup(surfID);
1253 if (!surf)
1254 {
1255 VUserLog("Error: IOSurfaceLookup(%d) failed.", surfID);
1256 return false;
1257 }
1258
1259 VuoShader_resetContext(context);
1260 bool ret = VuoImage_resolveInterprocessJsonOntoFramebufferInternal(surf, context, pixelsWide, pixelsHigh, flip, stretch);
1261
1263 CFRelease(surf);
1264 return ret;
1265}
1266
1270static GLuint CompileShader(CGLContextObj cgl_ctx, GLenum type, const char *source)
1271{
1272 GLint length = (GLint)strlen(source);
1273 GLuint shader = glCreateShader(type);
1274 glShaderSource(shader, 1, (const GLchar**)&source, &length);
1275 glCompileShader(shader);
1276
1277 int infologLength = 0;
1278 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLength);
1279 if (infologLength > 0)
1280 {
1281 char *infoLog = (char *)malloc(infologLength);
1282 int charsWritten = 0;
1283 glGetShaderInfoLog(shader, infologLength, &charsWritten, infoLog);
1284 VUserLog("%s", infoLog);
1285 free(infoLog);
1286 }
1287 return shader;
1288}
1289
1292
1296static bool VuoImage_resolveInterprocessJsonOntoFramebufferInternal(IOSurfaceRef surf, VuoGlContext context, GLsizei pixelsWide, GLsizei pixelsHigh, bool flip, bool stretch)
1297{
1298 CGLContextObj cgl_ctx = (CGLContextObj)context;
1299
1300 static bool openGL32Core;
1301 static GLuint vertexArray;
1302 static GLuint program;
1303 static GLuint receiveTextureOffsetAndSizeUniform;
1305 openGL32Core = VuoGlContext_isOpenGL32Core((VuoGlContext)cgl_ctx);
1306
1307 char *vertexShaderSource;
1308 char *fragmentShaderSource;
1309 if (openGL32Core)
1310 {
1311 // The following 2 `gl*VertexArrays` calls use the thread-local context (not CGLMacro).
1312 CGLSetCurrentContext(cgl_ctx);
1313
1314 glGenVertexArrays(1, &vertexArray);
1315 glBindVertexArray(vertexArray);
1316
1317 vertexShaderSource = VUOSHADER_GLSL_SOURCE(150,
1318 in vec2 position;
1319 in vec2 textureCoordinate;
1320 out vec2 fragmentTextureCoordinate;
1321 void main()
1322 {
1323 fragmentTextureCoordinate = textureCoordinate;
1324 gl_Position = vec4(position.x, position.y, 0., 1.);
1325 }
1326 );
1327 fragmentShaderSource = VUOSHADER_GLSL_SOURCE(150,
1328 uniform sampler2DRect receiveTexture;
1329 uniform vec4 receiveTextureOffsetAndSize;
1330 in vec2 fragmentTextureCoordinate;
1331 out vec4 FragColor;
1332 void main()
1333 {
1334 FragColor = texture(receiveTexture, receiveTextureOffsetAndSize.xy + fragmentTextureCoordinate * receiveTextureOffsetAndSize.zw);
1335 }
1336 );
1337 }
1338 else
1339 {
1340 // OpenGL 2.1 context.
1341
1342 glGenVertexArraysAPPLE(1, &vertexArray);
1343 glBindVertexArrayAPPLE(vertexArray);
1344
1345 vertexShaderSource = VUOSHADER_GLSL_SOURCE(120,
1346 attribute vec2 position;
1347 attribute vec2 textureCoordinate;
1348 varying vec2 fragmentTextureCoordinate;
1349 void main()
1350 {
1351 fragmentTextureCoordinate = textureCoordinate;
1352 gl_Position = vec4(position.x, position.y, 0., 1.);
1353 }
1354 );
1355 fragmentShaderSource = VUOSHADER_GLSL_SOURCE(120,
1356 uniform sampler2DRect receiveTexture;
1357 uniform vec4 receiveTextureOffsetAndSize;
1358 varying vec2 fragmentTextureCoordinate;
1359 void main()
1360 {
1361 gl_FragColor = texture2DRect(receiveTexture, receiveTextureOffsetAndSize.xy + fragmentTextureCoordinate * receiveTextureOffsetAndSize.zw);
1362 }
1363 );
1364 }
1365
1366
1367 const GLfloat quadPositionsAndTextureCoordinates[] = {
1368 // X Y U V
1369 -1, -1, 0, 0,
1370 1, -1, 1, 0,
1371 -1, 1, 0, 1,
1372
1373 1, 1, 1, 1,
1374 -1, 1, 0, 1,
1375 1, -1, 1, 0,
1376 };
1377 GLuint quadPTCBuffer;
1378 glGenBuffers(1, &quadPTCBuffer);
1379 glBindBuffer(GL_ARRAY_BUFFER, quadPTCBuffer);
1380 glBufferData(GL_ARRAY_BUFFER, sizeof(quadPositionsAndTextureCoordinates), quadPositionsAndTextureCoordinates, GL_STATIC_DRAW);
1381 VuoGlPool_logVRAMAllocated(sizeof(quadPositionsAndTextureCoordinates));
1382
1383 GLuint vertexShader = CompileShader(context, GL_VERTEX_SHADER, vertexShaderSource);
1384 GLuint fragmentShader = CompileShader(context, GL_FRAGMENT_SHADER, fragmentShaderSource);
1385 program = glCreateProgram();
1386 glAttachShader(program, vertexShader);
1387 glAttachShader(program, fragmentShader);
1388 glLinkProgram(program);
1389 GLuint positionAttribute = glGetAttribLocation(program, "position");
1390 GLuint textureCoordinateAttribute = glGetAttribLocation(program, "textureCoordinate");
1391 GLuint receiveTextureUniform = glGetUniformLocation(program, "receiveTexture");
1392 receiveTextureOffsetAndSizeUniform = glGetUniformLocation(program, "receiveTextureOffsetAndSize");
1393
1394 glUseProgram(program);
1395
1396 glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, (void*)0);
1397 glEnableVertexAttribArray(positionAttribute);
1398
1399 glVertexAttribPointer(textureCoordinateAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, (void*)(sizeof(GLfloat)*2));
1400 glEnableVertexAttribArray(textureCoordinateAttribute);
1401
1402 glUniform1i(receiveTextureUniform, 0);
1403 });
1404
1405
1406 if (openGL32Core)
1407 {
1408 CGLSetCurrentContext(cgl_ctx);
1409 glBindVertexArray(vertexArray);
1410 }
1411 else
1412 glBindVertexArrayAPPLE(vertexArray);
1413
1414 GLuint textureRect = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, pixelsWide, pixelsHigh, GL_BGRA, surf);
1415 if (!textureRect)
1416 {
1417 VUserLog("Error: Couldn't allocate texture.");
1418 VGL();
1419 return false;
1420 }
1421 VuoGlTexture_retain(textureRect, NULL, NULL);
1422
1423 glActiveTexture(GL_TEXTURE0);
1424 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, textureRect);
1425
1426 glUseProgram(program);
1427
1428 if (stretch)
1429 glUniform4f(receiveTextureOffsetAndSizeUniform, 0, flip ? pixelsHigh : 0, pixelsWide, pixelsHigh * (flip ? -1 : 1));
1430 else
1431 {
1432 // Center the image in the viewport.
1433 GLint viewport[4];
1434 glGetIntegerv(GL_VIEWPORT, viewport);
1435// VLog("Resolving %dx%d image onto a %dx%d viewport.", pixelsWide, pixelsHigh, viewport[2], viewport[3]);
1436 glUniform4f(receiveTextureOffsetAndSizeUniform,
1437 ((float)pixelsWide - viewport[2]) / 2,
1438 flip ? viewport[3] : 0,
1439 viewport[2],
1440 viewport[3] * (flip ? -1 : 1));
1441 }
1442
1443 glDrawArrays(GL_TRIANGLES, 0, 6);
1444
1445 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
1446 VuoGlTexture_release(VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, pixelsWide, pixelsHigh, textureRect);
1447 glUseProgram(0);
1448 if (openGL32Core)
1450 else
1451 glBindVertexArrayAPPLE(0);
1452
1453 glBindBuffer(GL_ARRAY_BUFFER, 0);
1454
1455 return true;
1456}
1457
1465{
1466 if (!value)
1467 return NULL;
1468
1469 VuoRetain(value);
1470
1471 json_object *js = json_object_new_object();
1472 json_object_object_add(js, "pointer", json_object_new_int64((int64_t)value));
1473 return js;
1474}
1475
1485{
1486 if (!value)
1487 return NULL;
1488
1489 __block VuoShader shader = NULL;
1490 __block IOSurfaceID surfID = 0;
1491 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1492// VLog("Creating an IOSurface from glTextureName %d on target %lu",value->glTextureName,value->glTextureTarget);
1493
1494 if (value->glTextureTarget == GL_TEXTURE_2D)
1495 shader = VuoShader_makeUnlitAlphaPassthruImageShader(value, false);
1496 else if (value->glTextureTarget == GL_TEXTURE_RECTANGLE_ARB)
1498 VuoRetain(shader);
1499
1500 surfID = VuoImageRenderer_draw_internal(shader, value->pixelsWide, value->pixelsHigh, VuoImage_getColorDepth(value), true, true, 0, NULL);
1501// VLog("Created IOSurfaceID %d",surfID);
1502
1503 // Ensure the command queue gets executed before we return,
1504 // since the IOSurface might immediately be used on another context.
1505 glFlushRenderAPPLE();
1506 });
1507 if (!surfID)
1508 return NULL;
1509
1510 json_object * js = json_object_new_object();
1511 {
1512 json_object * o = json_object_new_int(surfID);
1513 json_object_object_add(js, "ioSurface", o);
1514 }
1515
1516 {
1517 json_object * o = json_object_new_int64(value->pixelsWide);
1518 json_object_object_add(js, "pixelsWide", o);
1519 }
1520 {
1521 json_object * o = json_object_new_int64(value->pixelsHigh);
1522 json_object_object_add(js, "pixelsHigh", o);
1523 }
1524 {
1525 json_object *o = json_object_new_double(value->scaleFactor);
1526 json_object_object_add(js, "scaleFactor", o);
1527 }
1528 // VuoShader_makeUnlitImageShader retains the image; VuoRelease(shader) then releases it.
1529 // So don't release the shader until we're done with the image, in case this release is its last.
1530 VuoRelease(shader);
1531
1532 return js;
1533}
1534
1546{
1547 if (!value)
1548 return strdup("No image");
1549
1550 const char *type;
1551 switch (value->glInternalFormat)
1552 {
1553 case GL_RGB: type = "RGB, each channel 8-bit unsigned integer"; break;
1554 case GL_RGB16F_ARB: type = "RGB, each channel 16-bit signed float"; break;
1555 case GL_RGB32F_ARB: type = "RGB, each channel 32-bit signed float"; break;
1556 case GL_RGBA: type = "RGBA, each channel 8-bit unsigned integer"; break;
1557 case GL_RGBA16F_ARB: type = "RGBA, each channel 16-bit signed float"; break;
1558 case GL_RGBA32F_ARB: type = "RGBA, each channel 32-bit signed float"; break;
1559 case GL_LUMINANCE8: type = "intensity, 8-bit unsigned integer"; break;
1560 case GL_LUMINANCE16F_ARB: type = "intensity, 16-bit signed float"; break;
1561 case GL_LUMINANCE32F_ARB: type = "intensity, 32-bit signed float"; break;
1562 case GL_LUMINANCE8_ALPHA8: type = "intensity+alpha, each channel 8-bit unsigned integer"; break;
1563 case GL_LUMINANCE_ALPHA16F_ARB: type = "intensity+alpha, each channel 16-bit signed float"; break;
1564 case GL_LUMINANCE_ALPHA32F_ARB: type = "intensity+alpha, each channel 32-bit signed float"; break;
1565 case GL_DEPTH_COMPONENT: type = "intensity, 16-bit signed float"; break;
1566 default: type = "(unknown)";
1567 }
1568
1569 char *target = VuoGl_stringForConstant(value->glTextureTarget);
1570 char *internalformat = VuoGl_stringForConstant(value->glInternalFormat);
1571
1572 char *summary = VuoText_format("<div>%luĂ—%lu pixels @ %gx</div>\n<div>%s</div>\n<div>OpenGL: %s, %s, ID %u</div>",
1573 value->pixelsWide, value->pixelsHigh,
1574 value->scaleFactor,
1575 type,
1576 target,
1577 internalformat,
1578 value->glTextureName);
1579
1580 free(internalformat);
1581 free(target);
1582
1583 return summary;
1584}