Skip to content
This repository was archived by the owner on Jun 11, 2024. It is now read-only.

Commit d0c3e71

Browse files
committed
add image converter
1 parent 8cb7b45 commit d0c3e71

File tree

2 files changed

+62
-27
lines changed

2 files changed

+62
-27
lines changed

DevToys/DevToys/Body/Media/Image Converter/ImageConverter.swift

+53-13
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,26 @@ enum ImageConverter {
1919
try? FileManager.default.createDirectory(at: $0, withIntermediateDirectories: true, attributes: nil)
2020
}
2121

22-
static func convert(_ item: ImageItem, format: ImageFormatType, resize: Bool, size: CGSize, scale: ImageScaleMode, padding: Bool) -> ImageConvertTask {
23-
var resizeSize = item.image.size
22+
static func convert(_ item: ImageItem, format: ImageFormatType, resize: Bool, size: CGSize, scale: ImageScaleMode) -> ImageConvertTask {
23+
var image = item.image
24+
print(resize, size, scale)
2425
if resize {
25-
// switch scale {
26-
// case <#pattern#>:
27-
// <#code#>
28-
// default:
29-
// <#code#>
30-
// }
26+
switch scale {
27+
case .scaleToFill: if let rimage = image.resizedAspectFill(to: size) { image = rimage }
28+
case .scaleToFit: if let rimage = image.resizedAspectFit(to: size) { image = rimage }
29+
}
3130
}
3231

33-
// item.image.resized(to: <#T##NSSize#>)
3432
let isDone = Promise<Data, Error>.asyncError{ resolve, reject in
3533
switch format {
3634
case .png:
37-
guard let data = item.image.png else { return reject("Data failed.") }; resolve(data)
35+
guard let data = image.png else { return reject("Data failed.") }; resolve(data)
3836
case .jpg:
39-
guard let data = item.image.jpeg else { return reject("Data failed.") }; resolve(data)
37+
guard let data = image.jpeg else { return reject("Data failed.") }; resolve(data)
4038
case .tiff:
41-
guard let data = item.image.tiffRepresentation else { return reject("Data failed.") }; resolve(data)
39+
guard let data = image.tiffRepresentation else { return reject("Data failed.") }; resolve(data)
4240
case .gif:
43-
guard let data = item.image.gif else { return reject("Data failed.") }; resolve(data)
41+
guard let data = image.gif else { return reject("Data failed.") }; resolve(data)
4442
}
4543
}
4644
.tryPeek{ data in
@@ -101,6 +99,48 @@ extension Promise {
10199
extension NSImage {
102100
var pngData: Data { png! }
103101

102+
func resizedAspectFill(to newSize: CGSize) -> NSImage? {
103+
guard let bitmapRep = NSBitmapImageRep(
104+
bitmapDataPlanes: nil, pixelsWide: Int(newSize.width), pixelsHigh: Int(newSize.height),
105+
bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false,
106+
colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0
107+
) else { return nil }
108+
let scale = self.size.aspectFillRatio(fillInside: newSize)
109+
110+
bitmapRep.size = newSize
111+
NSGraphicsContext.saveGraphicsState()
112+
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmapRep)
113+
self.draw(in: CGRect(center: newSize.convertToPoint()/2, size: self.size * scale), from: .zero, operation: .copy, fraction: 1.0)
114+
NSGraphicsContext.restoreGraphicsState()
115+
116+
let resizedImage = NSImage(size: newSize)
117+
resizedImage.addRepresentation(bitmapRep)
118+
return resizedImage
119+
}
120+
121+
func resizedAspectFit(to newSize: CGSize) -> NSImage? {
122+
guard let bitmapRep = NSBitmapImageRep(
123+
bitmapDataPlanes: nil, pixelsWide: Int(newSize.width), pixelsHigh: Int(newSize.height),
124+
bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false,
125+
colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0
126+
) else { return nil }
127+
128+
let scale = self.size.aspectFitRatio(fitInside: newSize)
129+
130+
bitmapRep.size = newSize
131+
NSGraphicsContext.saveGraphicsState()
132+
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmapRep)
133+
NSColor.black.setFill()
134+
NSRect(size: newSize).fill()
135+
draw(in: CGRect(center: newSize.convertToPoint()/2, size: self.size * scale), from: .zero, operation: .copy, fraction: 1.0)
136+
NSGraphicsContext.restoreGraphicsState()
137+
138+
let resizedImage = NSImage(size: newSize)
139+
resizedImage.addRepresentation(bitmapRep)
140+
return resizedImage
141+
}
142+
143+
104144
func resized(to newSize: NSSize) -> NSImage {
105145
if let bitmapRep = NSBitmapImageRep(
106146
bitmapDataPlanes: nil, pixelsWide: Int(newSize.width), pixelsHigh: Int(newSize.height),

DevToys/DevToys/Body/Media/Image Converter/ImageConverterView+.swift

+9-14
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ final class ImageConverterViewController: PageViewController {
1515
@RestorableState("imc.resize") private var resize = false
1616
@RestorableState("imc.width") private var width = 1280.0
1717
@RestorableState("imc.height") private var height = 720.0
18-
@RestorableState("imc.width") private var padding = true
1918

2019
@Observable var task: [ImageConvertTask] = []
2120

@@ -34,8 +33,6 @@ final class ImageConverterViewController: PageViewController {
3433
.sink{[unowned self] in self.cell.widthField.value = $0 }.store(in: &objectBag)
3534
self.$height
3635
.sink{[unowned self] in self.cell.heightField.value = $0 }.store(in: &objectBag)
37-
self.$padding
38-
.sink{[unowned self] in self.cell.paddingSwitch.isOn = $0 }.store(in: &objectBag)
3936

4037
self.cell.resizeSwitch.isOnPublisher
4138
.sink{[unowned self] in self.resize = $0 }.store(in: &objectBag)
@@ -49,17 +46,17 @@ final class ImageConverterViewController: PageViewController {
4946
.sink{[unowned self] in self.format = $0 }.store(in: &objectBag)
5047
self.cell.dragPublisher
5148
.sink{[unowned self] in self.readURLs($0) }.store(in: &objectBag)
52-
self.cell.paddingSwitch.isOnPublisher
53-
.sink{[unowned self] in self.padding = $0 }.store(in: &objectBag)
5449
}
5550

5651
private func readURLs(_ pasteboard: NSPasteboard) {
5752
let newImageItems = ImageDropper.images(fromPasteboard: pasteboard)
5853
guard !newImageItems.isEmpty else { return }
5954

6055
self.task.append(contentsOf: newImageItems.map{
61-
ImageConverter.convert($0, format: format, resize: self.resize, size: [CGFloat(width), CGFloat(height)], scale: scaleMode, padding: padding)
62-
})
56+
ImageConverter.convert($0, format: format, resize: self.resize, size: [CGFloat(width), CGFloat(height)], scale: scaleMode)
57+
})
58+
59+
self.cell.listView.scrollView.contentView.scrollToBottom()
6360
}
6461
}
6562

@@ -73,14 +70,12 @@ enum ImageFormatType: String, TextItem {
7370
enum ImageScaleMode: String, TextItem {
7471
case scaleToFill = "Scale to Fill"
7572
case scaleToFit = "Scale to Fit"
76-
case stretch = "Stretch"
7773
}
7874

7975
final private class ImageConverterView: Page {
8076

8177
let formatTypePicker = EnumPopupButton<ImageFormatType>()
8278
let resizeSwitch = NSSwitch()
83-
let paddingSwitch = NSSwitch()
8479
let widthField = NumberField()
8580
let heightField = NumberField()
8681
let scaleModePicker = EnumPopupButton<ImageScaleMode>()
@@ -98,7 +93,6 @@ final private class ImageConverterView: Page {
9893
$0.addArrangedSubview(NSTextField(labelWithString: "x"))
9994
$0.addArrangedSubview(heightField)
10095
}))
101-
$0.addArrangedSubview(Area(title: "Padding", control: paddingSwitch))
10296
}
10397

10498
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
@@ -129,7 +123,7 @@ final private class ImageConverterView: Page {
129123
])
130124
)
131125

132-
self.addSection(Section(title: "Images", items: [listView]))
126+
self.addSection(Section(title: "Converted Images", items: [listView]))
133127
}
134128
}
135129

@@ -155,7 +149,7 @@ final private class ImageListView: NSLoadView {
155149

156150
extension ImageListView: NSTableViewDataSource, NSTableViewDelegate {
157151
func numberOfRows(in tableView: NSTableView) -> Int { convertTasks.count }
158-
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { 80 }
152+
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { 46 }
159153

160154
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
161155
let cell = ImageListCell()
@@ -200,14 +194,15 @@ final private class ImageListCell: NSLoadStackView {
200194
self.spacing = 16
201195
self.edgeInsets = .init(x: 16, y: 4)
202196
self.imageView.snp.makeConstraints{ make in
203-
make.width.equalTo(100)
204-
make.height.equalTo(64)
197+
make.width.equalTo(48)
198+
make.height.equalTo(28)
205199
}
206200

207201
let titleStack = NSStackView()
208202
self.addArrangedSubview(titleStack)
209203
titleStack.orientation = .vertical
210204
titleStack.alignment = .left
205+
titleStack.spacing = 4
211206
titleStack.distribution = .fillProportionally
212207
titleStack.addArrangedSubview(titleLabel)
213208
titleStack.addArrangedSubview(sizeLabel)

0 commit comments

Comments
 (0)