- 拆分 ContentDelegates/TextContentView 为独立协调器(InteractionCoordinator、LocationResolution、ExternalLinks、AttachmentTooltip) - 新增 RDEPUBAttachmentTooltipView/OverlayView 附件气泡提示 - 新增 RDEPUBDarkImageAdjuster 暗色模式图片亮度适配 - 新增 RDEPUBSelectionLoupeView 选区放大镜 - 新增 MetadataParseWorker/CancellationController 元数据解析取消机制 - 重构 PresentationRuntime/PaginationCoordinator 精简职责 - 优化 ChapterLoader/WarmupOrchestrator 异步章节加载 - CFI 模块微调与 NoteModels 更新 - 清理冗余文档,更新架构/UML/业务逻辑文档 Co-Authored-By: Claude <noreply@anthropic.com>
93 lines
3.8 KiB
Swift
93 lines
3.8 KiB
Swift
import UIKit
|
|
|
|
#if canImport(DTCoreText)
|
|
import DTCoreText
|
|
#endif
|
|
|
|
enum RDEPUBDarkImageAdjuster {
|
|
|
|
private static let imageCache = NSCache<NSString, UIImage>()
|
|
|
|
#if canImport(DTCoreText)
|
|
|
|
static func adjustIfNeeded(
|
|
_ content: NSMutableAttributedString,
|
|
configuration: RDEPUBReaderConfiguration
|
|
) -> NSMutableAttributedString {
|
|
guard configuration.darkImageAdjustmentEnabled,
|
|
configuration.darkImageBlendRatio > 0,
|
|
configuration.theme.contentBackgroundColor.rd_isDarkBackground else {
|
|
return content
|
|
}
|
|
|
|
let fullRange = NSRange(location: 0, length: content.length)
|
|
content.enumerateAttribute(.attachment, in: fullRange) { value, range, _ in
|
|
guard let attachment = value as? DTImageTextAttachment,
|
|
!isCoverAttachment(attachment),
|
|
let image = attachment.image,
|
|
shouldAdjust(image) else { return }
|
|
|
|
let adjustedAttachment = DTImageTextAttachment()
|
|
adjustedAttachment.image = adjustedImage(
|
|
image,
|
|
backgroundColor: configuration.theme.contentBackgroundColor,
|
|
blendRatio: configuration.darkImageBlendRatio,
|
|
cacheKey: cacheKey(for: attachment, image: image, configuration: configuration)
|
|
)
|
|
adjustedAttachment.originalSize = attachment.originalSize
|
|
adjustedAttachment.displaySize = attachment.displaySize
|
|
adjustedAttachment.verticalAlignment = attachment.verticalAlignment
|
|
adjustedAttachment.contentURL = attachment.contentURL
|
|
adjustedAttachment.hyperLinkURL = attachment.hyperLinkURL
|
|
adjustedAttachment.hyperLinkGUID = attachment.hyperLinkGUID
|
|
adjustedAttachment.attributes = attachment.attributes
|
|
content.addAttribute(.attachment, value: adjustedAttachment, range: range)
|
|
}
|
|
return content
|
|
}
|
|
|
|
private static func isCoverAttachment(_ attachment: DTTextAttachment) -> Bool {
|
|
let lowercasedClasses = ((attachment.attributes["class"] as? String) ?? "").lowercased()
|
|
let lowercasedPath = attachment.contentURL?.lastPathComponent.lowercased()
|
|
?? ((attachment.attributes["src"] as? String) ?? "").lowercased()
|
|
return lowercasedClasses.contains("rd-front-cover-image") || lowercasedPath.contains("cover")
|
|
}
|
|
|
|
private static func shouldAdjust(_ image: UIImage) -> Bool {
|
|
image.size.width >= 80 && image.size.height >= 80
|
|
}
|
|
|
|
private static func cacheKey(
|
|
for attachment: DTImageTextAttachment,
|
|
image: UIImage,
|
|
configuration: RDEPUBReaderConfiguration
|
|
) -> NSString {
|
|
let source = attachment.contentURL?.absoluteString
|
|
?? "\(Unmanaged.passUnretained(image).toOpaque())"
|
|
return "\(source)|\(image.size.width)x\(image.size.height)|\(configuration.theme.contentBackgroundColor.rd_cssString)|\(configuration.darkImageBlendRatio)" as NSString
|
|
}
|
|
|
|
private static func adjustedImage(
|
|
_ image: UIImage,
|
|
backgroundColor: UIColor,
|
|
blendRatio: CGFloat,
|
|
cacheKey: NSString
|
|
) -> UIImage {
|
|
if let cached = imageCache.object(forKey: cacheKey) { return cached }
|
|
let format = UIGraphicsImageRendererFormat()
|
|
format.scale = image.scale
|
|
format.opaque = false
|
|
let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
|
|
let adjusted = renderer.image { context in
|
|
image.draw(in: CGRect(origin: .zero, size: image.size))
|
|
backgroundColor.withAlphaComponent(max(0, min(0.35, blendRatio))).setFill()
|
|
context.cgContext.setBlendMode(.sourceAtop)
|
|
context.fill(CGRect(origin: .zero, size: image.size))
|
|
}
|
|
imageCache.setObject(adjusted, forKey: cacheKey)
|
|
return adjusted
|
|
}
|
|
|
|
#endif
|
|
}
|