Permalink
Browse files

added QuickLook plugins that render previews and thumbnails for QC an…

…d ISF content
  • Loading branch information...
1 parent 796a027 commit 65db0865ec06b1b05dfc17eab2ecad51a294e32a @mrRay committed May 5, 2016
Showing with 5,083 additions and 4 deletions.
  1. +56 −0 ISFQL/GeneratePreviewForURL.m
  2. +56 −0 ISFQL/GenerateThumbnailForURL.m
  3. +10 −0 ISFQL/ISFQL.entitlements
  4. +39 −0 ISFQL/ISFQLRendererRemote.h
  5. +154 −0 ISFQL/ISFQLRendererRemote.m
  6. +63 −0 ISFQL/Info.plist
  7. +2 −0 ISFQL/PackageInfo
  8. +218 −0 ISFQL/main.c
  9. +58 −0 ISFQL_Renderer/Assets.xcassets/AppIcon.appiconset/Contents.json
  10. +667 −0 ISFQL_Renderer/Base.lproj/MainMenu.xib
  11. BIN ISFQL_Renderer/ColorBars.png
  12. +44 −0 ISFQL_Renderer/ISFQLRendererAgent.h
  13. +253 −0 ISFQL_Renderer/ISFQLRendererAgent.m
  14. +10 −0 ISFQL_Renderer/ISFQL_Renderer.entitlements
  15. +38 −0 ISFQL_Renderer/ISFQL_RendererAppDelegate.h
  16. +138 −0 ISFQL_Renderer/ISFQL_RendererAppDelegate.m
  17. +29 −0 ISFQL_Renderer/ISFQL_RendererProtocols.h
  18. +40 −0 ISFQL_Renderer/Info.plist
  19. +23 −0 ISFQL_Renderer/com.vidvox.ISFQL-Renderer_DEBUG.plist
  20. +23 −0 ISFQL_Renderer/com.vidvox.ISFQL-Renderer_RELEASE.plist
  21. +13 −0 ISFQL_Renderer/main.m
  22. +56 −0 QCQL/GeneratePreviewForURL.m
  23. +56 −0 QCQL/GenerateThumbnailForURL.m
  24. +63 −0 QCQL/Info.plist
  25. +2 −0 QCQL/PackageInfo
  26. +10 −0 QCQL/QCQL.entitlements
  27. +39 −0 QCQL/QCQLRendererRemote.h
  28. +154 −0 QCQL/QCQLRendererRemote.m
  29. +218 −0 QCQL/main.c
  30. +58 −0 QCQL_Renderer/Assets.xcassets/AppIcon.appiconset/Contents.json
  31. +667 −0 QCQL_Renderer/Base.lproj/MainMenu.xib
  32. BIN QCQL_Renderer/ColorBars.png
  33. +40 −0 QCQL_Renderer/Info.plist
  34. +44 −0 QCQL_Renderer/QCQLRendererAgent.h
  35. +274 −0 QCQL_Renderer/QCQLRendererAgent.m
  36. +10 −0 QCQL_Renderer/QCQL_Renderer.entitlements
  37. +40 −0 QCQL_Renderer/QCQL_RendererAppDelegate.h
  38. +145 −0 QCQL_Renderer/QCQL_RendererAppDelegate.m
  39. +29 −0 QCQL_Renderer/QCQL_RendererProtocols.h
  40. +18 −0 QCQL_Renderer/SwizzleISF-RGBAtoBGRA.fs
  41. +23 −0 QCQL_Renderer/com.vidvox.QCQL-Renderer_DEBUG.plist
  42. +23 −0 QCQL_Renderer/com.vidvox.QCQL-Renderer_RELEASE.plist
  43. +13 −0 QCQL_Renderer/main.m
  44. +1,087 −4 VVOpenSource.xcodeproj/project.pbxproj
  45. +80 −0 VVOpenSource.xcodeproj/xcshareddata/xcschemes/QuickLook plugins.xcscheme
@@ -0,0 +1,56 @@
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#include <QuickLook/QuickLook.h>
+#import <Foundation/Foundation.h>
+#import "../ISFQL_Renderer/ISFQL_RendererProtocols.h"
+#import "ISFQLRendererRemote.h"
+
+
+OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options);
+void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview);
+
+/* -----------------------------------------------------------------------------
+ Generate a preview for file
+
+ This function's job is to create preview for designated file
+ ----------------------------------------------------------------------------- */
+
+OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options)
+{
+ NSString *pathToFile = [(NSURL *)url path];
+ //NSLog(@"%s ...%@",__func__,pathToFile);
+
+ ISFQLRendererRemote *renderer = [[ISFQLRendererRemote alloc] init];
+ [renderer renderThumbnailForPath:pathToFile sized:NSMakeSize(640,480)];
+ CGImageRef tmpImg = [renderer allocCGImageFromThumbnailData];
+ BOOL problemRendering = NO;
+ CGSize size = CGSizeMake(CGImageGetWidth(tmpImg), CGImageGetHeight(tmpImg));
+ if (size.width<=0 || size.height<=0) {
+ NSLog(@"\t\terr: problem rendering file %@",pathToFile);
+ problemRendering = YES;
+ }
+ else {
+ CGContextRef ctx = QLPreviewRequestCreateContext(preview, size, YES, nil);
+ if (ctx != NULL) {
+ CGContextDrawImage(ctx, CGRectMake(0, 0, size.width, size.height), tmpImg);
+ QLPreviewRequestFlushContext(preview, ctx);
+ CGContextRelease(ctx);
+ ctx = NULL;
+ }
+ }
+
+ CGImageRelease(tmpImg);
+ tmpImg = NULL;
+
+ [renderer prepareToBeDeleted];
+ [renderer release];
+ renderer = nil;
+
+ if (problemRendering)
+ return -3025;
+ else
+ return noErr;
+}
+void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview) {
+
+}
@@ -0,0 +1,56 @@
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#include <QuickLook/QuickLook.h>
+#import <Foundation/Foundation.h>
+#import "../ISFQL_Renderer/ISFQL_RendererProtocols.h"
+#import "ISFQLRendererRemote.h"
+
+
+OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize);
+void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail);
+
+/* -----------------------------------------------------------------------------
+ Generate a thumbnail for file
+
+ This function's job is to create thumbnail for designated file as fast as possible
+ ----------------------------------------------------------------------------- */
+
+OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)
+{
+ NSString *pathToFile = [(NSURL *)url path];
+ //NSLog(@"%s ...%@",__func__,pathToFile);
+
+ ISFQLRendererRemote *renderer = [[ISFQLRendererRemote alloc] init];
+ [renderer renderThumbnailForPath:pathToFile sized:NSMakeSize(maxSize.width, maxSize.height)];
+ CGImageRef tmpImg = [renderer allocCGImageFromThumbnailData];
+ BOOL problemRendering = NO;
+ CGSize size = CGSizeMake(CGImageGetWidth(tmpImg), CGImageGetHeight(tmpImg));
+ if (size.width<=0 || size.height<=0) {
+ NSLog(@"\t\terr: problem rendering file %@",pathToFile);
+ problemRendering = YES;
+ }
+ else {
+ CGContextRef ctx = QLThumbnailRequestCreateContext(thumbnail, size, true, nil);
+ if (ctx != NULL) {
+ CGContextDrawImage(ctx, CGRectMake(0, 0, size.width, size.height), tmpImg);
+ QLThumbnailRequestFlushContext(thumbnail, ctx);
+ CGContextRelease(ctx);
+ ctx = NULL;
+ }
+ }
+
+ CGImageRelease(tmpImg);
+ tmpImg = NULL;
+
+ [renderer prepareToBeDeleted];
+ [renderer release];
+ renderer = nil;
+
+ if (problemRendering)
+ return -3025;
+ else
+ return noErr;
+}
+void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail) {
+
+}
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.application-groups</key>
+ <array>
+ <string>$(TeamIdentifierPrefix)com.vidvox.ISFQL-Renderer</string>
+ </array>
+</dict>
+</plist>
@@ -0,0 +1,39 @@
+#import <Foundation/Foundation.h>
+#import "ISFQL_RendererProtocols.h"
+
+
+
+
+/* this is the object that you put in your app- it communicates with ISFQLRendererAgent, which is
+ a cocoa app running as a LaunchAgent. comm is handled via XPC via a mach service
+*/
+
+
+
+
+@interface ISFQLRendererRemote : NSObject <ISFQLService> {
+ BOOL deleted;
+
+ OSSpinLock connLock;
+ NSXPCConnection *conn;
+
+ NSData *thumbnailData;
+ NSSize thumbnailSize;
+}
+
+- (void) prepareToBeDeleted;
+
+// this method is asynchronous- call this, then call "allocCGImage/alloNSImage...", which will return
+// as soon as the remote process has rendered and passed back a frame.
+- (void) renderThumbnailForPath:(NSString *)n sized:(NSSize)s;
+
+@property (retain,readwrite) NSData *thumbnailData;
+@property (assign,readwrite) NSSize thumbnailSize;
+
+// stalls until it returns a frame or times out. the agent has a timeout, the app that owns the
+// agent has another timeout, and this method has a third timeout in these methods- it should
+// always return within a couple seconds, no matter what.
+- (CGImageRef) allocCGImageFromThumbnailData;
+- (NSImage *) allocNSImageFromThumbnailData;
+
+@end
@@ -0,0 +1,154 @@
+#import "ISFQLRendererRemote.h"
+#import <AppKit/AppKit.h>
+
+
+
+
+#define LOCK OSSpinLockLock
+#define UNLOCK OSSpinLockUnlock
+
+
+
+
+@implementation ISFQLRendererRemote
+
+
+- (id) init {
+ //NSLog(@"%s",__func__);
+ self = [super init];
+ if (self != nil) {
+ deleted = NO;
+ connLock = OS_SPINLOCK_INIT;
+ conn = nil;
+ thumbnailData = nil;
+ thumbnailSize = NSMakeSize(0,0);
+
+ // make and set up an XPC connection
+ LOCK(&connLock);
+ conn = [[NSXPCConnection alloc] initWithMachServiceName:@"com.vidvox.ISFQL-Renderer" options:0];
+ //NSLog(@"\t\tconn is %@",conn);
+ [conn setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(ISFQLAgentService)]];
+ [conn setExportedInterface:[NSXPCInterface interfaceWithProtocol:@protocol(ISFQLService)]];
+ [conn setExportedObject:self];
+ [conn setInvalidationHandler:^() {
+ //NSLog(@"%@ invalidation handler",[self className]);
+ }];
+ [conn setInterruptionHandler:^() {
+ //NSLog(@"%@ interruption handler",[self className]);
+ }];
+ [conn resume];
+ UNLOCK(&connLock);
+
+ // ping the remote object (this will launch the service if it doesn't already exist)
+ //[rop ping];
+ }
+ return self;
+}
+- (void) prepareToBeDeleted {
+ //NSLog(@"%s",__func__);
+
+ LOCK(&connLock);
+ if (conn != nil) {
+ //[conn suspend]; // DO NOT SUSPEND- for some reason, this prevents 'invalidate' from working.
+ [conn invalidate];
+ // if i don't explicitly set the exported object to nil, the exported object (self) just leaks.
+ [conn setExportedObject:nil];
+ [conn release];
+ conn = nil;
+ }
+ UNLOCK(&connLock);
+
+ deleted = YES;
+}
+- (void) dealloc {
+ //NSLog(@"%s",__func__);
+ if (!deleted)
+ [self prepareToBeDeleted];
+ [self setThumbnailData:nil];
+ [super dealloc];
+}
+
+
+@synthesize thumbnailData;
+@synthesize thumbnailSize;
+
+
+- (void) renderThumbnailForPath:(NSString *)n sized:(NSSize)s {
+ //NSLog(@"%s ... %@",__func__,n);
+ if (deleted)
+ return;
+ LOCK(&connLock);
+ id<ISFQLAgentService> rop = [conn remoteObjectProxy];
+ UNLOCK(&connLock);
+
+ [rop renderThumbnailForPath:n sized:s];
+}
+
+
+
+- (CGImageRef) allocCGImageFromThumbnailData {
+ // implement the timeout here
+ NSData *tmpData = nil;
+ NSDate *nowDate = [NSDate date];
+ while (tmpData == nil) {
+ tmpData = [self thumbnailData];
+ if (tmpData == nil)
+ [NSThread sleepForTimeInterval:1./10.];
+ if (fabs([nowDate timeIntervalSinceNow])>=4.)
+ break;
+ }
+ // if we couldn't get any data and we're here, we've timed out- return nil immediately
+ if (tmpData == nil)
+ return NULL;
+
+ NSSize renderSize = [self thumbnailSize];
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)tmpData);
+ CGImageRef img = (provider==NULL) ? NULL : CGImageCreate(renderSize.width,
+ renderSize.height,
+ 8,
+ 32,
+ 32*renderSize.width/8,
+ colorSpace, // colorspace
+ kCGBitmapByteOrderDefault | kCGImageAlphaLast,
+ provider, // provider
+ NULL,
+ true,
+ kCGRenderingIntentPerceptual);
+ if (img == NULL)
+ NSLog(@"\t\terr: img NULL in %s",__func__);
+
+ if (provider != NULL) {
+ CGDataProviderRelease(provider);
+ provider = NULL;
+ }
+ if (colorSpace != NULL) {
+ CGColorSpaceRelease(colorSpace);
+ colorSpace = NULL;
+ }
+
+ return img;
+}
+
+- (NSImage *) allocNSImageFromThumbnailData {
+ CGImageRef cgImg = [self allocCGImageFromThumbnailData];
+ if (cgImg == NULL)
+ return nil;
+ NSImage *returnMe = [[NSImage alloc] initWithCGImage:cgImg size:NSMakeSize(CGImageGetWidth(cgImg), CGImageGetHeight(cgImg))];
+ CGImageRelease(cgImg);
+ cgImg = NULL;
+ return returnMe;
+}
+
+
+#pragma mark ISFQLService
+
+
+- (void) renderedBitmapData:(NSData *)d sized:(NSSize)s {
+ //NSLog(@"%s ... %d x %d",__func__,(int)s.width,(int)s.height);
+ [self setThumbnailData:d];
+ [self setThumbnailSize:s];
+}
+
+
+@end
View
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>QLGenerator</string>
+ <key>LSItemContentTypes</key>
+ <array>
+ <string>org.khronos.glsl.fragment-shader</string>
+ </array>
+ </dict>
+ </array>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>CFPlugInDynamicRegisterFunction</key>
+ <string></string>
+ <key>CFPlugInDynamicRegistration</key>
+ <string>NO</string>
+ <key>CFPlugInFactories</key>
+ <dict>
+ <key>C9142621-646A-4B45-B716-98351C84DC13</key>
+ <string>QuickLookGeneratorPluginFactory</string>
+ </dict>
+ <key>CFPlugInTypes</key>
+ <dict>
+ <key>5E2D9680-5022-40FA-B806-43349622E5B9</key>
+ <array>
+ <string>C9142621-646A-4B45-B716-98351C84DC13</string>
+ </array>
+ </dict>
+ <key>CFPlugInUnloadFunction</key>
+ <string></string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2016 vidvox. All rights reserved.</string>
+ <key>QLNeedsToBeRunInMainThread</key>
+ <false/>
+ <key>QLPreviewHeight</key>
+ <real>600</real>
+ <key>QLPreviewWidth</key>
+ <real>800</real>
+ <key>QLSupportsConcurrentRequests</key>
+ <false/>
+ <key>QLThumbnailMinimumSize</key>
+ <integer>16</integer>
+</dict>
+</plist>
View
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<pkg-info postinstall-action="restart"/>
Oops, something went wrong.

0 comments on commit 65db086

Please sign in to comment.