From 4f106a679d2bee4473712452e13853a8033820e3 Mon Sep 17 00:00:00 2001 From: SeiyaTozaki Date: Tue, 19 May 2026 17:59:54 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E4=B8=A6=E5=88=97AVIF=E3=83=87?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E6=99=82=E3=81=AE=E3=82=AF=E3=83=A9?= =?UTF-8?q?=E3=83=83=E3=82=B7=E3=83=A5=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nukeが複数スレッドでAVIF画像をデコードする際、ColorSpaceキャッシュの Dictionary CoW競合でクラッシュしていた問題をNSLockで保護して修正。 あわせてYUV422のypCrDiffBufferのバッファ参照ミスと、 ダミーCrプレーンの確保サイズ不整合も修正。 Co-authored-by: Cursor --- .../Buffer/BufferConversion.swift | 2 +- .../Buffer/BufferExtraction.swift | 2 +- Nuke-Avif-Plugin/ColorSpace.swift | 50 +++++++++++++------ 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/Nuke-Avif-Plugin/Buffer/BufferConversion.swift b/Nuke-Avif-Plugin/Buffer/BufferConversion.swift index 58446fe..b5df90c 100644 --- a/Nuke-Avif-Plugin/Buffer/BufferConversion.swift +++ b/Nuke-Avif-Plugin/Buffer/BufferConversion.swift @@ -137,7 +137,7 @@ func converter8( ypBufferData1.deallocate() } - var ypCrDiffBuffer = vImage_Buffer(data: ypCbDiffBufferData, height: yp.pointee.height, width: avif.vWidth / 2, rowBytes: Int(alignedWidth) / 2 * MemoryLayout.size) + var ypCrDiffBuffer = vImage_Buffer(data: ypCrDiffBufferData, height: yp.pointee.height, width: avif.vWidth / 2, rowBytes: Int(alignedWidth) / 2 * MemoryLayout.size) let ypCrDiffBufferArray = UnsafeMutablePointer?>.allocate(capacity: 1) ypCrDiffBufferArray.initialize(to: &ypCrDiffBuffer) defer { diff --git a/Nuke-Avif-Plugin/Buffer/BufferExtraction.swift b/Nuke-Avif-Plugin/Buffer/BufferExtraction.swift index 4814c8f..f5e5e09 100644 --- a/Nuke-Avif-Plugin/Buffer/BufferExtraction.swift +++ b/Nuke-Avif-Plugin/Buffer/BufferExtraction.swift @@ -52,7 +52,7 @@ func extract8(avif: avifImage, chromaShift: (x: Int, y: Int), pixelRange: vImage [] ) } else { - let dummyCrData = UnsafeMutableRawPointer.allocate(byteCount: cbcrWidth * MemoryLayout.size, alignment: 0) + let dummyCrData = UnsafeMutableRawPointer.allocate(byteCount: cbcrWidth * MemoryLayout.size, alignment: 0) dummyCrData.initializeMemory(as: UInt8.self, repeating: UInt8(pixelRange.CbCr_bias), count: cbcrWidth) return ( .init(data: dummyCrData, diff --git a/Nuke-Avif-Plugin/ColorSpace.swift b/Nuke-Avif-Plugin/ColorSpace.swift index 98d4ed5..affe8cb 100644 --- a/Nuke-Avif-Plugin/ColorSpace.swift +++ b/Nuke-Avif-Plugin/ColorSpace.swift @@ -7,8 +7,37 @@ import CoreGraphics import Accelerate +import Foundation import libavif +private enum ColorSpaceCache { + private static let lock = NSLock() + private static var rgbColorSpaces: [CFString: CGColorSpace] = [:] + private static var monochromeColorSpaces: [String: CGColorSpace] = [:] + + static func rgb(identifier: CFString, create: () -> CGColorSpace) -> CGColorSpace { + lock.lock() + defer { lock.unlock() } + if let cached = rgbColorSpaces[identifier] { + return cached + } + let colorSpace = create() + rgbColorSpaces[identifier] = colorSpace + return colorSpace + } + + static func monochrome(identifier: String, create: () throws -> CGColorSpace) throws -> CGColorSpace { + lock.lock() + defer { lock.unlock() } + if let cached = monochromeColorSpaces[identifier] { + return cached + } + let colorSpace = try create() + monochromeColorSpaces[identifier] = colorSpace + return colorSpace + } +} + func calcRGBPrimaries(colorPrimaries: avifColorPrimaries) -> vImageRGBPrimaries { var primaries = [Float](repeating: 0, count: 8) avifColorPrimariesGetValues(colorPrimaries, &primaries) @@ -45,7 +74,6 @@ func createColorSpaceRGB(colorPrimaries: avifColorPrimaries, transferCharacteris } private let defaultColorSpaceRGB = CGColorSpaceCreateDeviceRGB() -private var rgbColorSpaces = [CFString : CGColorSpace]() func calcColorSpaceRGB(avif: avifImage) throws -> CGColorSpace { if avif.icc.data != nil && avif.icc.size > 0 { guard let iccData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, avif.icc.data, avif.icc.size, kCFAllocatorNull) else { throw ColorSpaceError(message: "cfdata creation failed.") } @@ -54,12 +82,8 @@ func calcColorSpaceRGB(avif: avifImage) throws -> CGColorSpace { } func cachedColorSpace(identifier: CFString) -> CGColorSpace { - if let colorSpace = rgbColorSpaces[identifier] { - return colorSpace - } else { - let colorSpace = CGColorSpace(name: identifier)! - rgbColorSpaces[identifier] = colorSpace - return colorSpace + ColorSpaceCache.rgb(identifier: identifier) { + CGColorSpace(name: identifier)! } } @@ -130,7 +154,6 @@ func createColorSpaceMonochrome(colorPrimaries: avifColorPrimaries, transferChar } private let defaultColorSpaceMonochrome = CGColorSpaceCreateDeviceGray() -private var monochromeColorSpaces = [String : CGColorSpace]() func calcColorSpaceMonochrome(avif: avifImage) throws -> CGColorSpace { if avif.icc.data != nil && avif.icc.size > 0 { guard let iccData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, avif.icc.data, avif.icc.size, kCFAllocatorNull) else { throw ColorSpaceError(message: "cfdata creation failed.") } @@ -139,12 +162,11 @@ func calcColorSpaceMonochrome(avif: avifImage) throws -> CGColorSpace { } func cachedColorSpace(identifier: String) throws -> CGColorSpace { - if let colorSpace = monochromeColorSpaces[identifier] { - return colorSpace - } else { - let colorSpace = try createColorSpaceMonochrome(colorPrimaries: avif.colorPrimaries, transferCharacteristics: avif.transferCharacteristics) - monochromeColorSpaces[identifier] = colorSpace - return colorSpace + try ColorSpaceCache.monochrome(identifier: identifier) { + try createColorSpaceMonochrome( + colorPrimaries: avif.colorPrimaries, + transferCharacteristics: avif.transferCharacteristics + ) } } From 4149dba75c798ca5f29ad5e3d3a6df8db84b16ea Mon Sep 17 00:00:00 2001 From: SeiyaTozaki Date: Tue, 26 May 2026 15:35:05 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor(colorspace):=E9=87=8D=E8=A4=87?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=84=E3=81=9FUNKNOWN=20case=E3=82=921?= =?UTF-8?q?=E8=A1=8C=E3=81=AB=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- Nuke-Avif-Plugin/ColorSpace.swift | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Nuke-Avif-Plugin/ColorSpace.swift b/Nuke-Avif-Plugin/ColorSpace.swift index affe8cb..54a2c02 100644 --- a/Nuke-Avif-Plugin/ColorSpace.swift +++ b/Nuke-Avif-Plugin/ColorSpace.swift @@ -88,10 +88,7 @@ func calcColorSpaceRGB(avif: avifImage) throws -> CGColorSpace { } switch (avif.colorPrimaries, avif.transferCharacteristics) { - case (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN), - (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN), - (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN), - (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN): + case (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN): return defaultColorSpaceRGB case (AVIF_COLOR_PRIMARIES_BT709, AVIF_TRANSFER_CHARACTERISTICS_BT709): @@ -171,10 +168,7 @@ func calcColorSpaceMonochrome(avif: avifImage) throws -> CGColorSpace { } switch (avif.colorPrimaries, avif.transferCharacteristics) { - case (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN), - (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN), - (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN), - (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN): + case (AVIF_COLOR_PRIMARIES_UNKNOWN, AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN): return defaultColorSpaceMonochrome case (AVIF_COLOR_PRIMARIES_BT709, AVIF_TRANSFER_CHARACTERISTICS_BT709):