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

Commit

Permalink
add image converter
Browse files Browse the repository at this point in the history
  • Loading branch information
ObuchiYuki committed Feb 4, 2022
1 parent 8cb7b45 commit d0c3e71
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 27 deletions.
66 changes: 53 additions & 13 deletions DevToys/DevToys/Body/Media/Image Converter/ImageConverter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,26 @@ enum ImageConverter {
try? FileManager.default.createDirectory(at: $0, withIntermediateDirectories: true, attributes: nil)
}

static func convert(_ item: ImageItem, format: ImageFormatType, resize: Bool, size: CGSize, scale: ImageScaleMode, padding: Bool) -> ImageConvertTask {
var resizeSize = item.image.size
static func convert(_ item: ImageItem, format: ImageFormatType, resize: Bool, size: CGSize, scale: ImageScaleMode) -> ImageConvertTask {
var image = item.image
print(resize, size, scale)
if resize {
// switch scale {
// case <#pattern#>:
// <#code#>
// default:
// <#code#>
// }
switch scale {
case .scaleToFill: if let rimage = image.resizedAspectFill(to: size) { image = rimage }
case .scaleToFit: if let rimage = image.resizedAspectFit(to: size) { image = rimage }
}
}

// item.image.resized(to: <#T##NSSize#>)
let isDone = Promise<Data, Error>.asyncError{ resolve, reject in
switch format {
case .png:
guard let data = item.image.png else { return reject("Data failed.") }; resolve(data)
guard let data = image.png else { return reject("Data failed.") }; resolve(data)
case .jpg:
guard let data = item.image.jpeg else { return reject("Data failed.") }; resolve(data)
guard let data = image.jpeg else { return reject("Data failed.") }; resolve(data)
case .tiff:
guard let data = item.image.tiffRepresentation else { return reject("Data failed.") }; resolve(data)
guard let data = image.tiffRepresentation else { return reject("Data failed.") }; resolve(data)
case .gif:
guard let data = item.image.gif else { return reject("Data failed.") }; resolve(data)
guard let data = image.gif else { return reject("Data failed.") }; resolve(data)
}
}
.tryPeek{ data in
Expand Down Expand Up @@ -101,6 +99,48 @@ extension Promise {
extension NSImage {
var pngData: Data { png! }

func resizedAspectFill(to newSize: CGSize) -> NSImage? {
guard let bitmapRep = NSBitmapImageRep(
bitmapDataPlanes: nil, pixelsWide: Int(newSize.width), pixelsHigh: Int(newSize.height),
bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false,
colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0
) else { return nil }
let scale = self.size.aspectFillRatio(fillInside: newSize)

bitmapRep.size = newSize
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmapRep)
self.draw(in: CGRect(center: newSize.convertToPoint()/2, size: self.size * scale), from: .zero, operation: .copy, fraction: 1.0)
NSGraphicsContext.restoreGraphicsState()

let resizedImage = NSImage(size: newSize)
resizedImage.addRepresentation(bitmapRep)
return resizedImage
}

func resizedAspectFit(to newSize: CGSize) -> NSImage? {
guard let bitmapRep = NSBitmapImageRep(
bitmapDataPlanes: nil, pixelsWide: Int(newSize.width), pixelsHigh: Int(newSize.height),
bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false,
colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0
) else { return nil }

let scale = self.size.aspectFitRatio(fitInside: newSize)

bitmapRep.size = newSize
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmapRep)
NSColor.black.setFill()
NSRect(size: newSize).fill()
draw(in: CGRect(center: newSize.convertToPoint()/2, size: self.size * scale), from: .zero, operation: .copy, fraction: 1.0)
NSGraphicsContext.restoreGraphicsState()

let resizedImage = NSImage(size: newSize)
resizedImage.addRepresentation(bitmapRep)
return resizedImage
}


func resized(to newSize: NSSize) -> NSImage {
if let bitmapRep = NSBitmapImageRep(
bitmapDataPlanes: nil, pixelsWide: Int(newSize.width), pixelsHigh: Int(newSize.height),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ final class ImageConverterViewController: PageViewController {
@RestorableState("imc.resize") private var resize = false
@RestorableState("imc.width") private var width = 1280.0
@RestorableState("imc.height") private var height = 720.0
@RestorableState("imc.width") private var padding = true

@Observable var task: [ImageConvertTask] = []

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

self.cell.resizeSwitch.isOnPublisher
.sink{[unowned self] in self.resize = $0 }.store(in: &objectBag)
Expand All @@ -49,17 +46,17 @@ final class ImageConverterViewController: PageViewController {
.sink{[unowned self] in self.format = $0 }.store(in: &objectBag)
self.cell.dragPublisher
.sink{[unowned self] in self.readURLs($0) }.store(in: &objectBag)
self.cell.paddingSwitch.isOnPublisher
.sink{[unowned self] in self.padding = $0 }.store(in: &objectBag)
}

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

self.task.append(contentsOf: newImageItems.map{
ImageConverter.convert($0, format: format, resize: self.resize, size: [CGFloat(width), CGFloat(height)], scale: scaleMode, padding: padding)
})
ImageConverter.convert($0, format: format, resize: self.resize, size: [CGFloat(width), CGFloat(height)], scale: scaleMode)
})

self.cell.listView.scrollView.contentView.scrollToBottom()
}
}

Expand All @@ -73,14 +70,12 @@ enum ImageFormatType: String, TextItem {
enum ImageScaleMode: String, TextItem {
case scaleToFill = "Scale to Fill"
case scaleToFit = "Scale to Fit"
case stretch = "Stretch"
}

final private class ImageConverterView: Page {

let formatTypePicker = EnumPopupButton<ImageFormatType>()
let resizeSwitch = NSSwitch()
let paddingSwitch = NSSwitch()
let widthField = NumberField()
let heightField = NumberField()
let scaleModePicker = EnumPopupButton<ImageScaleMode>()
Expand All @@ -98,7 +93,6 @@ final private class ImageConverterView: Page {
$0.addArrangedSubview(NSTextField(labelWithString: "x"))
$0.addArrangedSubview(heightField)
}))
$0.addArrangedSubview(Area(title: "Padding", control: paddingSwitch))
}

override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
Expand Down Expand Up @@ -129,7 +123,7 @@ final private class ImageConverterView: Page {
])
)

self.addSection(Section(title: "Images", items: [listView]))
self.addSection(Section(title: "Converted Images", items: [listView]))
}
}

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

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

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cell = ImageListCell()
Expand Down Expand Up @@ -200,14 +194,15 @@ final private class ImageListCell: NSLoadStackView {
self.spacing = 16
self.edgeInsets = .init(x: 16, y: 4)
self.imageView.snp.makeConstraints{ make in
make.width.equalTo(100)
make.height.equalTo(64)
make.width.equalTo(48)
make.height.equalTo(28)
}

let titleStack = NSStackView()
self.addArrangedSubview(titleStack)
titleStack.orientation = .vertical
titleStack.alignment = .left
titleStack.spacing = 4
titleStack.distribution = .fillProportionally
titleStack.addArrangedSubview(titleLabel)
titleStack.addArrangedSubview(sizeLabel)
Expand Down

0 comments on commit d0c3e71

Please sign in to comment.