Fast Thumbnails With CGImageSource
The parameters are documented, but the optimal combination is not. Here’s what I learned:
kCGImageSourceCreateThumbnailFromImageAlways: While this seems optional for correct functionality, without it there will be error logged for images that might include embedded thumbnails (like JPEG or HEIC), but don’t. There’s aFromImageIfAbsentvariant, but it did not silence these logs in all cases.kCGImageSourceCreateThumbnailWithTransform: Required for the thumbnail to respect the EXIF orientation of the image, in case it has one (which is not uncommon for JPEG).kCGImageSourceThumbnailMaxPixelSize: Specifies the largest dimension of the desired thumbnail; the returned image will then be equal or smaller than this.The first test showed incredible improvements: The same 12MP JPEG image now took just about 26ms on macOS. That’s almost 30 times faster than the naive approach.
Image IO has some very useful APIs, although unfortunately it hasn’t always been well tested. I ended up writing four different versions of an image resizer because different parts of the API would fail on certain files with different macOS versions. The resizer keeps trying different techniques until it finds one that works. Also, sometimes the APIs raise C++ exceptions. If you don’t catch these from Objective-C using @catch (...) (literally, three periods), your app will crash.
Avoid bug in
CGImageSourceCreateImageAtIndexwith indexed-color (4-bit palette) ICO files — always useCGImageSourceCreateThumbnailAtIndex.
Previously:
Update (2026-04-21): Paul Goracke:
A while ago, @bdudney released a wonderful little ebook titled “All the Image IO You Need to Know” which had this info as well as more. Certainly changed my coding.
4 Comments RSS · Twitter · Mastodon
I saw this was about thumbnails and immediately though of my old thumbnailing code…and it's been so long that I forgot I used vImage instead of CGImage! I've not tested performance in a decade, so it would be interesting to see how it stacks up against modern APIs. I profiled the snot out of the framework with Shark and MallocDebug, since I was producing scaled thumbnails at multiple sizes, but vImage was not easy to use.
https://github.com/amaxwell/fileview/blob/master/FVCGImageUtilities.mm
https://github.com/amaxwell/fileview/blob/master/FVImageBuffer.h
There are also APIs to cache the image and/or prepare for display. Depending on what the image is needed for, enabling or disabling those can also help.
To expand on the above comment, the keys are:
kCGImageSourceShouldCache
kCGImageSourceShouldCacheImmediately