Skip to content

Commit

Permalink
Fix msgtracer leak (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
henrik-dmg authored Aug 24, 2020
1 parent 8a580ff commit 8e754c9
Show file tree
Hide file tree
Showing 15 changed files with 76 additions and 67 deletions.
4 changes: 2 additions & 2 deletions Sources/SwiftFrame/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ struct SwiftFrame: ParsableCommand {
static let configuration = CommandConfiguration(
commandName: "swiftframe",
abstract: "CLI application for speedy screenshot framing",
version: "3.1.0",
version: "3.1.1",
helpNames: .shortAndLong)

@Argument(help: "Read configuration values from the specified file", completion: .list(["config", "yml", "yaml"]))
@Argument(help: "Read configuration values from the specified file", completion: .list(["config", "json", "yml", "yaml"]))
var configFilePath: String

@Flag(name: .shortAndLong, help: "Prints additional information and lets you verify the config file before rendering")
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftFrameCore/Config/ScreenshotData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct ScreenshotData: Decodable, ConfigValidatable, Equatable {
// MARK: - Misc

func makeProcessedData(size: CGSize) -> ScreenshotData {
return ScreenshotData(
ScreenshotData(
screenshotName: screenshotName,
bottomLeft: bottomLeft.convertingToBottomLeftOrigin(withSize: size),
bottomRight: bottomRight.convertingToBottomLeftOrigin(withSize: size),
Expand Down
37 changes: 37 additions & 0 deletions Sources/SwiftFrameCore/Helper Types/GraphicsContext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import AppKit
import CoreGraphics
import Foundation

class GraphicsContext {

let cg: CGContext
private let colorSpace: CGColorSpace

lazy var ci: CIContext = {
CIContext(cgContext: cg, options: [
CIContextOption.workingColorSpace: colorSpace,
CIContextOption.useSoftwareRenderer: false
])
}()


init(size: CGSize) throws {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let context = CGContext(
data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: 8,
bytesPerRow: 0,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
)

guard let cgContext = context else {
throw NSError(description: "Failed to create graphics context")
}
self.cg = cgContext
self.colorSpace = colorSpace
}

}
4 changes: 2 additions & 2 deletions Sources/SwiftFrameCore/Workers/ConfigProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ public class ConfigProcessor {
color: data.textColorSource.color,
maxFontSize: data.maxFontSize)

printVerbose("Writing images for device \"\(deviceData.outputSuffix)\" for locale \"\(locale)\" asynchronously...")

try ImageWriter.finish(
context: composer.context,
with: data.outputPaths,
Expand All @@ -113,6 +111,8 @@ public class ConfigProcessor {
suffix: deviceData.outputSuffix,
format: data.outputFormat)

print("Finished \(locale)-\(deviceData.outputSuffix)")

group.leave()
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftFrameCore/Workers/DecodableParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct DecodableParser {

init?(rawValue: String) {
switch rawValue {
case "json":
case "config", "json":
self = .json
case "yml", "yaml":
self = .yaml
Expand Down
27 changes: 5 additions & 22 deletions Sources/SwiftFrameCore/Workers/ImageComposer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,12 @@ final class ImageComposer {
private let textRenderer = TextRenderer()
private let screenshotRenderer = ScreenshotRenderer()

let context: CGContext
let context: GraphicsContext

// MARK: - Init

init(canvasSize: CGSize) throws {
self.context = try ImageComposer.createContext(size: canvasSize)
}

// MARK: - Preparation

private static func createContext(size: CGSize) throws -> CGContext {
guard let context = CGContext(
data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: 8,
bytesPerRow: 0,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
else {
throw NSError(description: "Failed to create graphics context")
}
return context
self.context = try GraphicsContext(size: canvasSize)
}

// MARK: - Composition
Expand All @@ -49,9 +32,9 @@ final class ImageComposer {
throw NSError(description: "Could not render template image")
}

context.saveGState()
context.draw(templateImage, in: image.ky_nativeRect)
context.restoreGState()
context.cg.saveGState()
context.cg.draw(templateImage, in: image.ky_nativeRect)
context.cg.restoreGState()
}

// MARK: - Titles Rendering
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftFrameCore/Workers/ImageWriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public final class ImageWriter {
// MARK: - Exporting

static func finish(
context: CGContext,
context: GraphicsContext,
with outputPaths: [FileURL],
sliceSize: CGSize,
gapWidth: Int,
Expand All @@ -15,7 +15,7 @@ public final class ImageWriter {
suffix: String,
format: FileFormat) throws
{
guard let image = context.makeImage() else {
guard let image = context.cg.makeImage() else {
throw NSError(description: "Could not render output image")
}
let slices = try sliceImage(image, with: sliceSize, gapWidth: gapWidth)
Expand Down
14 changes: 7 additions & 7 deletions Sources/SwiftFrameCore/Workers/ScreenshotRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ final class ScreenshotRenderer {

// MARK: - Screenshot Rendering

func render(screenshot: NSBitmapImageRep, with data: ScreenshotData, in context: CGContext) throws {
let cgImage = try renderScreenshot(screenshot, with: data)
func render(screenshot: NSBitmapImageRep, with data: ScreenshotData, in context: GraphicsContext) throws {
let cgImage = try renderScreenshot(screenshot, with: data, in: context)
let rect = calculateRect(for: data)

context.saveGState()
context.draw(cgImage, in: rect)
context.restoreGState()
context.cg.saveGState()
context.cg.draw(cgImage, in: rect)
context.cg.restoreGState()
}

private func renderScreenshot(_ screenshot: NSBitmapImageRep, with data: ScreenshotData) throws -> CGImage {
private func renderScreenshot(_ screenshot: NSBitmapImageRep, with data: ScreenshotData, in context: GraphicsContext) throws -> CGImage {
let ciImage = CIImage(bitmapImageRep: screenshot)

let perspectiveTransform = CIFilter(name: "CIPerspectiveTransform")!
Expand All @@ -27,7 +27,7 @@ final class ScreenshotRenderer {

guard
let compositeImage = perspectiveTransform.outputImage,
let cgImage = CIContext().createCGImage(compositeImage, from: calculateRect(for: data))
let cgImage = context.ci.createCGImage(compositeImage, from: calculateRect(for: data))
else {
throw NSError(description: "Could not skew screenshot")
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/SwiftFrameCore/Workers/TextRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ final class TextRenderer {

// MARK: - Frame Rendering

func render(text: String, font: NSFont, color: NSColor, alignment: TextAlignment, rect: NSRect, context: CGContext) throws {
func render(text: String, font: NSFont, color: NSColor, alignment: TextAlignment, rect: NSRect, context: GraphicsContext) throws {
guard !text.isEmpty else {
print(CommandLineFormatter.formatWarning(text: "String was emtpy and will not be rendered"))
return
}

let attributedString = try makeAttributedString(for: text, font: font, color: color, alignment: alignment)

context.saveGState()
context.cg.saveGState()

let frame = try makeFrame(from: attributedString, in: rect, alignment: alignment)
CTFrameDraw(frame, context)
context.restoreGState()
CTFrameDraw(frame, context.cg)
context.cg.restoreGState()
}

private func makeFrame(from attributedString: NSAttributedString, in rect: NSRect, alignment: TextAlignment) throws -> CTFrame {
Expand Down
8 changes: 4 additions & 4 deletions Tests/SwiftFrameTests/ImageComposerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ class ImageComposerTests: XCTestCase {

func testRenderTemplateFile() throws {
let size = CGSize(width: 100, height: 50)
let templateFile = CGContext.makeImageRepWithSize(size)
let templateFile = try GraphicsContext(size: size).cg.makePlainWhiteImageRep()
let composer = try ImageComposer(canvasSize: size)
try composer.addTemplateImage(templateFile)

let image = try ky_unwrap(composer.context.makeImage())
let image = try ky_unwrap(composer.context.cg.makeImage())
XCTAssertEqual(image.width, Int(size.width))
XCTAssertEqual(image.height, Int(size.height))
}

func testTemplateImageSlicesCorrectly() throws {
let size = CGSize(width: 100, height: 50)
let templateFile = CGContext.makeImageRepWithSize(size)
let templateFile = try GraphicsContext(size: size).cg.makePlainWhiteImageRep()
let composer = try ImageComposer(canvasSize: size)
try composer.addTemplateImage(templateFile)

let image = try ky_unwrap(composer.context.makeImage())
let image = try ky_unwrap(composer.context.cg.makeImage())
let slices = try ImageWriter.sliceImage(image, with: NSSize(width: 20, height: 50), gapWidth: 0)
XCTAssertEqual(slices.count, 5)
for slice in slices {
Expand Down
3 changes: 2 additions & 1 deletion Tests/SwiftFrameTests/ImageLoaderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import XCTest
class ImageLoaderTests: BaseTest {

func testLoadImage() throws {
let rep = CGContext.makeImageRepWithSize(.square100Pixels)
let context = try GraphicsContext(size: .square100Pixels)
let rep = context.cg.makePlainWhiteImageRep()
let cgImage = try ky_unwrap(rep.cgImage)

try ImageWriter.write(cgImage, to: "testing/", locale: "en", deviceID: "testing_device", format: .png)
Expand Down
4 changes: 2 additions & 2 deletions Tests/SwiftFrameTests/ScreenshotRendererTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ class ScreenshotRendererTests: XCTestCase {

func testRenderScreenshot() throws {
let size = CGSize(width: 100, height: 100)
let context = CGContext.with(size: size)
let rep = CGContext.makeImageRepWithSize(size)
let context = try GraphicsContext(size: size)
let rep = context.cg.makePlainWhiteImageRep()

let mockScreenshotData = ScreenshotData.goodData

Expand Down
2 changes: 1 addition & 1 deletion Tests/SwiftFrameTests/TextRendererTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TextRendererTests: XCTestCase {
let size = CGSize(width: 100, height: 100)
let rect = NSRect(x: 10, y: 10, width: 80, height: 80)

let context = CGContext.with(size: size)
let context = try GraphicsContext(size: size)
try renderer.render(
text: "Some title",
font: .systemFont(ofSize: 20),
Expand Down
20 changes: 4 additions & 16 deletions Tests/SwiftFrameTests/Utility/TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,10 @@ extension Dictionary where Value == String, Key == String {

extension CGContext {

static func with(size: CGSize) -> CGContext {
CGContext(
data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: 8,
bytesPerRow: 0,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
}

static func makeImageRepWithSize(_ size: CGSize) -> NSBitmapImageRep {
let context = CGContext.with(size: size)
context.setFillColor(.white)
context.fill(NSRect(x: 0, y: 0, width: size.width, height: size.height))
return NSBitmapImageRep(cgImage: context.makeImage()!)
func makePlainWhiteImageRep() -> NSBitmapImageRep {
setFillColor(.white)
fill(NSRect(x: 0, y: 0, width: width, height: height))
return NSBitmapImageRep(cgImage: makeImage()!)
}

}
Expand Down
4 changes: 2 additions & 2 deletions Tests/SwiftFrameTests/Utility/TestingUtility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public typealias JSONDictionary = [String: Encodable]
struct TestingUtility {

static func writeMockScreenshot(locale: String, deviceSuffix: String) throws {
let rep = CGContext.makeImageRepWithSize(.square100Pixels)
let rep = try GraphicsContext(size: .square100Pixels).cg.makePlainWhiteImageRep()
guard let cgImage = rep.cgImage else {
throw NSError(description: "Could not make CGImage from Bitmap")
}
Expand All @@ -16,7 +16,7 @@ struct TestingUtility {
}

static func writeMockTemplateFile(deviceSuffix: String, gapWidth: Int) throws {
let rep = CGContext.makeImageRepWithSize(.make100PixelsSize(with: gapWidth, numberOfGaps: 4))
let rep = try GraphicsContext(size: .make100PixelsSize(with: gapWidth, numberOfGaps: 4)).cg.makePlainWhiteImageRep()
guard let cgImage = rep.cgImage else {
throw NSError(description: "Could not make CGImage from Bitmap")
}
Expand Down

0 comments on commit 8e754c9

Please sign in to comment.