@@ -18,8 +18,11 @@ enum ImageUtils {
1818 quality: CGFloat = 0.8 ,
1919 source: String ? = nil
2020 ) -> Data ? {
21- guard let image = NSImage ( data: imageData) ,
22- let cgImage = image. cgImage ( forProposedRect: nil , context: nil , hints: nil ) else {
21+ #if arch(x86_64)
22+ return compressImageIntel ( from: imageData, maxDimension: maxDimension, quality: quality, source: source)
23+ #else
24+ guard let imageSource = CGImageSourceCreateWithData ( imageData as CFData , nil ) ,
25+ let cgImage = CGImageSourceCreateImageAtIndex ( imageSource, 0 , nil ) else {
2326 let context = source. map { " from \( $0) " } ?? " "
2427 Logger . warning ( " Failed to create image \( context) ( \( imageData. count) bytes) " )
2528 return nil
@@ -64,6 +67,7 @@ enum ImageUtils {
6467 let logContext = source. map { " from \( $0) " } ?? " "
6568 Logger . warning ( " HEIC encoding failed \( logContext) , falling back to JPEG " )
6669 return resizeImage ( from: imageData, to: targetSize)
70+ #endif
6771 }
6872
6973 /// Encode a CGImage as HEIC data.
@@ -85,6 +89,25 @@ enum ImageUtils {
8589 return data as Data
8690 }
8791
92+ /// Encode a CGImage as JPEG data.
93+ static func encodeJPEG( _ cgImage: CGImage , quality: CGFloat = 0.8 ) -> Data ? {
94+ let data = NSMutableData ( )
95+ guard let destination = CGImageDestinationCreateWithData (
96+ data as CFMutableData ,
97+ UTType . jpeg. identifier as CFString ,
98+ 1 ,
99+ nil
100+ ) else { return nil }
101+ CGImageDestinationAddImage ( destination, cgImage, [
102+ kCGImageDestinationLossyCompressionQuality: quality
103+ ] as CFDictionary )
104+ guard CGImageDestinationFinalize ( destination) else {
105+ Logger . warning ( " Failed to encode image as JPEG " )
106+ return nil
107+ }
108+ return data as Data
109+ }
110+
88111 /// Resize an image from Data to specified size and return as JPEG Data
89112 /// - Parameters:
90113 /// - imageData: Original image data
@@ -117,8 +140,7 @@ enum ImageUtils {
117140
118141 guard let resizedCG = context. makeImage ( ) else { return nil }
119142
120- let bitmapRep = NSBitmapImageRep ( cgImage: resizedCG)
121- return bitmapRep. representation ( using: . jpeg, properties: [ . compressionFactor: compressionFactor] )
143+ return encodeJPEG ( resizedCG, quality: CGFloat ( compressionFactor) )
122144 }
123145
124146 /// Extract dominant colors from image data using grid-based sampling with diversity selection.
@@ -391,6 +413,42 @@ enum ImageUtils {
391413 guard let cgImage = ctx. makeImage ( ) else { return nil }
392414 return NSBitmapImageRep ( cgImage: cgImage) . representation ( using: . jpeg, properties: [ . compressionFactor: 0.85 ] )
393415 }
416+
417+ // MARK: - Intel x86_64 fallback
418+ // Software HEVC encode (VCPHEVC) on Intel deadlocks under concurrent scans (issue #265).
419+ // Resize-and-JPEG bypasses the encoder entirely. Remove this block when Intel support is dropped.
420+
421+ #if arch(x86_64)
422+ private static func compressImageIntel(
423+ from imageData: Data ,
424+ maxDimension: CGFloat ,
425+ quality: CGFloat ,
426+ source: String ?
427+ ) -> Data ? {
428+ guard let imageSource = CGImageSourceCreateWithData ( imageData as CFData , nil ) ,
429+ let props = CGImageSourceCopyPropertiesAtIndex ( imageSource, 0 , nil ) as? [ CFString : Any ] ,
430+ let width = props [ kCGImagePropertyPixelWidth] as? CGFloat ,
431+ let height = props [ kCGImagePropertyPixelHeight] as? CGFloat else {
432+ let context = source. map { " from \( $0) " } ?? " "
433+ Logger . warning ( " Failed to read image properties \( context) ( \( imageData. count) bytes) " )
434+ return nil
435+ }
436+
437+ var destWidth = width
438+ var destHeight = height
439+ if width > maxDimension || height > maxDimension {
440+ let scale = min ( maxDimension / width, maxDimension / height)
441+ destWidth = ( width * scale) . rounded ( . down)
442+ destHeight = ( height * scale) . rounded ( . down)
443+ }
444+
445+ return resizeImage (
446+ from: imageData,
447+ to: NSSize ( width: destWidth, height: destHeight) ,
448+ compressionFactor: Float ( quality)
449+ )
450+ }
451+ #endif
394452}
395453
396454// MARK: - Color Cache Object
0 commit comments