{"id":51644,"date":"2026-04-19T10:45:10","date_gmt":"2026-04-19T14:45:10","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=51644"},"modified":"2026-04-19T10:46:35","modified_gmt":"2026-04-19T14:46:35","slug":"fast-thumbnails-with-cgimagesource","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2026\/04\/19\/fast-thumbnails-with-cgimagesource\/","title":{"rendered":"Fast Thumbnails With CGImageSource"},"content":{"rendered":"<p><a href=\"https:\/\/macguru.dev\/fast-thumbnails-with-cgimagesource\/\">Max Seelemann<\/a>:<\/p>\n<blockquote cite=\"https:\/\/macguru.dev\/fast-thumbnails-with-cgimagesource\/\">\n<p>The parameters are documented, but the optimal combination is not. Here&rsquo;s what I learned:<\/p><ul><li><code>kCGImageSourceCreateThumbnailFromImageAlways<\/code>: While this seems optional for correct functionality, without it there will be error logged for images that <em>might<\/em> include embedded thumbnails (like JPEG or HEIC), but don&rsquo;t. There&rsquo;s a <code>FromImageIfAbsent<\/code> variant, but it did not silence these logs in all cases.<\/li><li><code>kCGImageSourceCreateThumbnailWithTransform<\/code>: Required for the thumbnail to respect the EXIF orientation of the image, in case it has one (which is not uncommon for JPEG).<\/li><li><code>kCGImageSourceThumbnailMaxPixelSize<\/code>: Specifies the <em>largest<\/em> dimension of the desired thumbnail; the returned image will then be equal or smaller than this.<\/li><\/ul><p>The first test showed incredible improvements: The same 12MP JPEG image now took just about <strong>26ms<\/strong> on macOS. That&rsquo;s almost <strong>30 times faster<\/strong> than the naive approach.<\/p>\n<\/blockquote>\n\n<p>Image I\/O has some very useful APIs, although unfortunately it hasn&rsquo;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&rsquo;t catch these from Objective-C using <code>@catch (...)<\/code> (literally, three periods), your app will crash.<\/p>\n\n<p><a href=\"https:\/\/github.com\/Ranchero-Software\/NetNewsWire\/commit\/5adf4d26e9d3f539a79c92d73b348df4d9ed187e\">Brent Simmons<\/a>:<\/p>\n<blockquote cite=\"https:\/\/github.com\/Ranchero-Software\/NetNewsWire\/commit\/5adf4d26e9d3f539a79c92d73b348df4d9ed187e\"><p>Avoid bug in <code>CGImageSourceCreateImageAtIndex<\/code> with indexed-color (4-bit palette) ICO files &mdash;&nbsp;always use <code>CGImageSourceCreateThumbnailAtIndex<\/code>.<\/p><\/blockquote>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2019\/12\/09\/cgimagesource-memory-leak\/\">CGImageSource Memory Leak<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2005\/09\/20\/jpeg-2000\/\">JPEG 2000<\/a><\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Max Seelemann: The parameters are documented, but the optimal combination is not. Here&rsquo;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&rsquo;t. There&rsquo;s a FromImageIfAbsent variant, but it did not silence these logs in [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2026-04-19T14:45:14Z","apple_news_api_id":"e7bce7f0-d50a-4feb-ae69-1353df7e72d1","apple_news_api_modified_at":"2026-04-19T14:46:38Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAA==","apple_news_api_share_url":"https:\/\/apple.news\/A57zn8NUKT-uuaRNT335y0Q","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[619,31,2741,30,2742,54,71,178,901],"class_list":["post-51644","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-graphics","tag-ios","tag-ios-26","tag-mac","tag-macos-tahoe-26","tag-objective-c","tag-programming","tag-quartz","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/51644","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=51644"}],"version-history":[{"count":2,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/51644\/revisions"}],"predecessor-version":[{"id":51646,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/51644\/revisions\/51646"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=51644"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=51644"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=51644"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}