diff --git a/SpreadPaper/Services/NSImage+PixelSize.swift b/SpreadPaper/Services/NSImage+PixelSize.swift new file mode 100644 index 0000000..a0df946 --- /dev/null +++ b/SpreadPaper/Services/NSImage+PixelSize.swift @@ -0,0 +1,17 @@ +import AppKit + +extension NSImage { + nonisolated var pixelSize: CGSize { + if let bitmapRep = representations + .compactMap({ $0 as? NSBitmapImageRep }) + .max(by: { ($0.pixelsWide * $0.pixelsHigh) < ($1.pixelsWide * $1.pixelsHigh) }) { + return CGSize(width: bitmapRep.pixelsWide, height: bitmapRep.pixelsHigh) + } + + if let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) { + return CGSize(width: cgImage.width, height: cgImage.height) + } + + return size + } +} diff --git a/SpreadPaper/Services/WallpaperManager.swift b/SpreadPaper/Services/WallpaperManager.swift index 1578a03..7c8598a 100644 --- a/SpreadPaper/Services/WallpaperManager.swift +++ b/SpreadPaper/Services/WallpaperManager.swift @@ -384,7 +384,8 @@ class WallpaperManager { deviceScale: CGFloat, screenColorSpace: CGColorSpace? ) throws -> CGImage { - var rect = CGRect(origin: .zero, size: original.size) + let imageSize = original.pixelSize + var rect = CGRect(origin: .zero, size: imageSize) guard let cgImage = original.cgImage(forProposedRect: &rect, context: nil, hints: nil) else { throw WallpaperError.imageConversionFailed } @@ -409,8 +410,8 @@ class WallpaperManager { let realOffsetX_Px = (offset.width / previewScale) * deviceScale let realOffsetY_Px = (offset.height / previewScale) * deviceScale - let drawnImgWidthPx = original.size.width * imageScale * deviceScale - let drawnImgHeightPx = original.size.height * imageScale * deviceScale + let drawnImgWidthPx = imageSize.width * imageScale * deviceScale + let drawnImgHeightPx = imageSize.height * imageScale * deviceScale let totalCanvasWidthPx = totalCanvas.width * deviceScale let totalCanvasHeightPx = totalCanvas.height * deviceScale let centeringX_Px = (totalCanvasWidthPx - drawnImgWidthPx) / 2.0 diff --git a/SpreadPaper/Views/EditorCanvasView.swift b/SpreadPaper/Views/EditorCanvasView.swift index 792147c..b655c69 100644 --- a/SpreadPaper/Views/EditorCanvasView.swift +++ b/SpreadPaper/Views/EditorCanvasView.swift @@ -27,13 +27,14 @@ struct EditorCanvasView: View { ZStack { // Image layer if let img = selectedImage { + let pixelSize = img.pixelSize Image(nsImage: img) .resizable() .aspectRatio(contentMode: .fill) .scaleEffect(x: isFlipped ? -1 : 1, y: 1) .frame( - width: img.size.width * previewScale * imageScale, - height: img.size.height * previewScale * imageScale + width: pixelSize.width * previewScale * imageScale, + height: pixelSize.height * previewScale * imageScale ) .offset(imageOffset) .opacity(isDragging ? 0.7 : 1.0) @@ -47,7 +48,7 @@ struct EditorCanvasView: View { ) imageOffset = calculateSnapping( raw: raw, - imgSize: img.size, + imgSize: pixelSize, canvasSize: manager.totalCanvas.size, previewScale: previewScale, zoomScale: imageScale diff --git a/SpreadPaper/Views/EditorView.swift b/SpreadPaper/Views/EditorView.swift index 43b94f0..0812531 100644 --- a/SpreadPaper/Views/EditorView.swift +++ b/SpreadPaper/Views/EditorView.swift @@ -431,7 +431,7 @@ struct EditorView: View { private var staticSubtitle: String { guard let img = loadedImages.first else { return "Choose image…" } - return "\(Int(img.size.width))×\(Int(img.size.height))" + return dimensions(for: img) } private func appearanceImageRow(index: Int, label: String) -> some View { @@ -499,7 +499,8 @@ struct EditorView: View { } private func dimensions(for image: NSImage) -> String { - "\(Int(image.size.width))×\(Int(image.size.height))" + let pixelSize = image.pixelSize + return "\(Int(pixelSize.width))×\(Int(pixelSize.height))" } // MARK: - Zoom @@ -646,8 +647,9 @@ struct EditorView: View { guard let image = currentImage else { return } let canvas = manager.totalCanvas guard canvas.width > 0, canvas.height > 0 else { return } - let widthRatio = canvas.width / image.size.width - let heightRatio = canvas.height / image.size.height + let pixelSize = image.pixelSize + let widthRatio = canvas.width / pixelSize.width + let heightRatio = canvas.height / pixelSize.height withAnimation(.spring()) { guard selectedVariantIndex < variants.count else { return } variants[selectedVariantIndex].scale = max(widthRatio, heightRatio) diff --git a/SpreadPaper/Views/GalleryView.swift b/SpreadPaper/Views/GalleryView.swift index adc7f31..f76ef85 100644 --- a/SpreadPaper/Views/GalleryView.swift +++ b/SpreadPaper/Views/GalleryView.swift @@ -699,17 +699,17 @@ private struct ThumbnailResult: @unchecked Sendable { let image: NSImage } -@Sendable -private func renderThumbnails(jobs: [ThumbnailJob]) -> [ThumbnailResult] { +nonisolated private func renderThumbnails(jobs: [ThumbnailJob]) -> [ThumbnailResult] { var out: [ThumbnailResult] = [] out.reserveCapacity(jobs.count) for job in jobs { guard let image = NSImage(contentsOf: job.imageURL) else { continue } let maxDim: CGFloat = 480 - let ratio = min(maxDim / image.size.width, maxDim / image.size.height, 1.0) + let pixelSize = image.pixelSize + let ratio = min(maxDim / pixelSize.width, maxDim / pixelSize.height, 1.0) let newSize = NSSize( - width: image.size.width * ratio, - height: image.size.height * ratio + width: pixelSize.width * ratio, + height: pixelSize.height * ratio ) let thumb = NSImage(size: newSize) thumb.lockFocus()