Skip to content

Instantly share code, notes, and snippets.

@rsms
Created October 4, 2016 00:31
Show Gist options
  • Save rsms/206eca5c41ab565153ee8ca11620ffe3 to your computer and use it in GitHub Desktop.
Save rsms/206eca5c41ab565153ee8ca11620ffe3 to your computer and use it in GitHub Desktop.
QuickLook icon generation
#pragma once
#include <Foundation/Foundation.h>
#include "common.h"
@interface IconRequest : NSObject
@property (readonly) BOOL canceled;
@property (readonly) CGImageRef image; // null if canceled or failed
- (void)cancel;
@end
struct IconStore {
IconStore(){}
IconRequest* requestIcon(NSString* path,
double scaleFactor,
double maxWidthPt,
double maxHeightPt,
void(^cb)(IconRequest*));
};
extern IconStore iconStore;
#import "IconStore.h"
#import <AppKit/AppKit.h>
#import <QuickLook/QuickLook.h>
IconStore iconStore;
@interface IconRequest (Internal)
- (instancetype)initWithRequestID:(QLThumbnailRef)requestID;
- (void)nullRequestID;
- (void)setIsCanceled;
- (void)setImage:(CGImageRef)img;
@end
IconRequest* IconStore::requestIcon(NSString* path,
double scaleFactor,
double maxWidthPt,
double maxHeightPt,
void(^cb)(IconRequest*))
{
// [BUG] Don't use kQLThumbnailOptionIconModeKey — it will cause random failures.
auto options = @{
(__bridge NSString*)kQLThumbnailOptionScaleFactorKey: @(scaleFactor),
};
auto cfurl = (__bridge CFURLRef)[NSURL fileURLWithPath:path];
QLThumbnailRef thumb = QLThumbnailCreate(kCFAllocatorDefault, cfurl, {maxWidthPt,maxHeightPt}, (__bridge CFDictionaryRef)options);
auto req = [[IconRequest alloc] initWithRequestID:thumb];
CFRelease(thumb); // now solely referenced by `req`
QLThumbnailDispatchAsync(thumb, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (QLThumbnailIsCancelled(thumb)) {
dispatch_async(dispatch_get_main_queue(), ^{
[req setIsCanceled];
cb(req);
});
} else {
auto img = QLThumbnailCopyImage(thumb);
dispatch_async(dispatch_get_main_queue(), ^{
[req setImage:img];
if (img) {
CGImageRelease(img);
}
cb(req);
});
}
});
return req;
}
@implementation IconRequest {
@public
QLThumbnailRef _requestID;
BOOL _isCanceled;
CGImageRef _img;
}
@synthesize canceled = _isCanceled;
@synthesize image = _img;
- (instancetype)initWithRequestID:(QLThumbnailRef)requestID {
self = [super init];
_requestID = requestID;
_isCanceled = NO;
CFRetain(_requestID);
return self;
}
- (void)dealloc {
if (_requestID) {
CFRelease((QLThumbnailRef)_requestID);
}
if (_img) {
CGImageRelease(_img);
}
}
- (void)cancel {
_isCanceled = YES;
if (_requestID) {
QLThumbnailCancel((QLThumbnailRef)_requestID);
CFRelease((QLThumbnailRef)_requestID);
_requestID = nullptr;
}
}
- (void)nullRequestID {
if (_requestID) {
CFRelease((QLThumbnailRef)_requestID);
_requestID = nullptr;
}
}
- (void)setIsCanceled {
_isCanceled = YES;
}
- (void)setImage:(CGImageRef)img {
auto oldImg = _img;
_img = img;
if (_img) {
CGImageRetain(_img);
}
if (oldImg) {
CGImageRelease(oldImg);
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment