@@ -557,10 +557,9 @@ extension TPPBook {
557557
558558 TPPBookCoverRegistryBridge . shared. thumbnailImageForBook ( self ) { [ weak self] image in
559559 guard let self = self else { return }
560- let final = image ?? UIImage ( systemName: " book " )
561560
562- self . thumbnailImage = final
563- if let img = final {
561+ self . thumbnailImage = image
562+ if let img = image {
564563 self . imageCache. set ( img, for: self . identifier)
565564 self . imageCache. set ( img, for: thumbnailKey)
566565 if self . coverImage == nil {
@@ -595,38 +594,69 @@ extension TPPBook {
595594
596595// MARK: - Dominant Color (async, off main thread)
597596private extension TPPBook {
597+ private static let colorProcessingQueue = DispatchQueue ( label: " org.thepalaceproject.dominantcolor " , qos: . utility)
598+ private static let sharedCIContext : CIContext = {
599+ guard let colorSpace = CGColorSpace ( name: CGColorSpace . sRGB) else {
600+ return CIContext ( )
601+ }
602+ return CIContext ( options: [
603+ . workingColorSpace: colorSpace,
604+ . outputColorSpace: colorSpace,
605+ . useSoftwareRenderer: false
606+ ] )
607+ } ( )
608+
598609 func updateDominantColor( using image: UIImage ) {
599610 let inputImage = image
600- DispatchQueue . global ( qos : . userInitiated ) . async { [ weak self] in
611+ Self . colorProcessingQueue . async { [ weak self] in
601612 guard let self = self else { return }
602613
603- let ciImage = CIImage ( image: inputImage)
604- let filter = CIFilter . areaAverage ( )
605- filter. inputImage = ciImage
606- filter. extent = ciImage? . extent ?? . zero
607-
608- guard let outputImage = filter. outputImage else { return }
609-
610- var bitmap = [ UInt8] ( repeating: 0 , count: 4 )
611- let context = CIContext ( options: [ CIContextOption . useSoftwareRenderer: false ] )
612- context. render (
613- outputImage,
614- toBitmap: & bitmap,
615- rowBytes: 4 ,
616- bounds: CGRect ( x: 0 , y: 0 , width: 1 , height: 1 ) ,
617- format: . RGBA8,
618- colorSpace: nil
619- )
614+ autoreleasepool {
615+ guard let ciImage = CIImage ( image: inputImage) else {
616+ Log . debug ( #file, " Failed to create CIImage from UIImage for book: \( self . identifier) " )
617+ return
618+ }
620619
621- let color = UIColor (
622- red: CGFloat ( bitmap [ 0 ] ) / 255.0 ,
623- green: CGFloat ( bitmap [ 1 ] ) / 255.0 ,
624- blue: CGFloat ( bitmap [ 2 ] ) / 255.0 ,
625- alpha: CGFloat ( bitmap [ 3 ] ) / 255.0
626- )
620+ guard !ciImage. extent. isEmpty else {
621+ Log . debug ( #file, " CIImage has empty extent for book: \( self . identifier) " )
622+ return
623+ }
627624
628- DispatchQueue . main. async {
629- self . dominantUIColor = color
625+ let filter = CIFilter . areaAverage ( )
626+ filter. inputImage = ciImage
627+ filter. extent = ciImage. extent
628+
629+ guard let outputImage = filter. outputImage else {
630+ Log . debug ( #file, " Failed to generate output image from filter for book: \( self . identifier) " )
631+ return
632+ }
633+
634+ guard let colorSpace = CGColorSpace ( name: CGColorSpace . sRGB) else {
635+ Log . debug ( #file, " Failed to create sRGB color space for book: \( self . identifier) " )
636+ return
637+ }
638+
639+ var bitmap = [ UInt8] ( repeating: 0 , count: 4 )
640+
641+ Self . sharedCIContext. render (
642+ outputImage,
643+ toBitmap: & bitmap,
644+ rowBytes: 4 ,
645+ bounds: CGRect ( x: 0 , y: 0 , width: 1 , height: 1 ) ,
646+ format: . RGBA8,
647+ colorSpace: colorSpace
648+ )
649+
650+ let color = UIColor (
651+ red: CGFloat ( bitmap [ 0 ] ) / 255.0 ,
652+ green: CGFloat ( bitmap [ 1 ] ) / 255.0 ,
653+ blue: CGFloat ( bitmap [ 2 ] ) / 255.0 ,
654+ alpha: CGFloat ( bitmap [ 3 ] ) / 255.0
655+ )
656+
657+ DispatchQueue . main. async {
658+ self . dominantUIColor = color
659+ }
630660 }
631661 }
632662 }
0 commit comments