Skip to content

Commit

Permalink
Merge pull request #153 from cocoatype/122-mismatch-in-recognized-and…
Browse files Browse the repository at this point in the history
…-detected-text-boxes

Fix mismatch in recognized vs. detected text boxes
  • Loading branch information
Arclite authored Jun 22, 2024
2 parents ef0811b + 89b1bd4 commit 60be353
Show file tree
Hide file tree
Showing 40 changed files with 870 additions and 404 deletions.
37 changes: 0 additions & 37 deletions Automator/Sources/BrushStampFactory.swift

This file was deleted.

3 changes: 2 additions & 1 deletion Automator/Sources/RedactActionExportError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
enum RedactActionExportError: Error {
case failedToGenerateGraphicsContext
case noImageForInput
case operationReturnedNoResult
case writeError
case failedToGetBitmapRepresentation
case failedToGetData
}
117 changes: 0 additions & 117 deletions Automator/Sources/RedactActionExportOperation.swift

This file was deleted.

30 changes: 27 additions & 3 deletions Automator/Sources/RedactOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
// Copyright © 2020 Cocoatype, LLC. All rights reserved.

import DetectionsMac
import ExportingMac
import Foundation
import OSLog
import Redacting
import RedactionsMac
import AppKit

class RedactOperation: Operation {
class RedactOperation: Operation, @unchecked Sendable {
var result: Result<String, Error>?
init(input: RedactActionInput, wordList: [String]) {
self.input = input
Expand All @@ -27,8 +30,29 @@ class RedactOperation: Operation {

let redactions = matchingObservations.map { Redaction($0, color: .black) }

RedactActionExporter.export(input, redactions: redactions) { [weak self] result in
self?.finish(with: result)
Task { [weak self] in
do {
guard let inputImage = input.image else { throw RedactActionExportError.noImageForInput }
let redactedImage = try await PhotoExportRenderer(image: inputImage, redactions: redactions).render()
let writeURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString, conformingTo: input.fileType ?? .png)

os_log("export representations: %{public}@", String(describing: redactedImage.representations))

guard let cgImage = redactedImage.cgImage(forProposedRect: nil, context: nil, hints: nil)
else { throw RedactActionExportError.failedToGetBitmapRepresentation }

let imageRep = NSBitmapImageRep(cgImage: cgImage)

guard let data = imageRep.representation(using: input.imageType, properties: [:])
else { throw RedactActionExportError.failedToGetData }

try data.write(to: writeURL)

self?.finish(with: .success(writeURL.path))
} catch {
os_log("export error occured: %{public}@", String(describing: error))
self?.finish(with: .failure(error))
}
}
}
}
Expand Down
24 changes: 0 additions & 24 deletions Automator/Sources/RedactedImageExporter.swift

This file was deleted.

92 changes: 65 additions & 27 deletions Modules/Capabilities/Brushes/Sources/BrushStampFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,77 @@ import AppKit
import ErrorHandlingMac
import GeometryMac

class BrushStampFactory: NSObject {
static func brushStart(scaledToHeight height: CGFloat, color: NSColor) -> NSImage {
guard let standardImage = Bundle(for: Self.self).image(forResource: "Brush Start") else { fatalError("Unable to load brush start image") }
return scaledImage(from: standardImage, toHeight: height, color: color)
public enum BrushStampFactory {
public static func brushImages(for shape: Shape, color: NSColor, scale: CGFloat) throws -> (CGImage, CGImage) {
let height = shape.unionDotShapeDotShapeDotUnionCrash.geometryStreamer.height
let startImage = try BrushStampFactory.brushStart(scaledToHeight: height, color: color)
let endImage = try BrushStampFactory.brushEnd(scaledToHeight: height, color: color)

return (startImage, endImage)
}

public static func brushStart(scaledToHeight height: CGFloat, color: NSColor) throws -> CGImage {
guard let standardImage = Bundle.module.image(forResource: "Brush Start") else { ErrorHandler().crash("Unable to load brush start image") }
return try scaledImage(from: standardImage, toHeight: height, color: color)
}

static func brushEnd(scaledToHeight height: CGFloat, color: NSColor) -> NSImage {
guard let standardImage = Bundle(for: Self.self).image(forResource: "Brush End") else { fatalError("Unable to load brush end image") }
return scaledImage(from: standardImage, toHeight: height, color: color)
public static func brushEnd(scaledToHeight height: CGFloat, color: NSColor) throws -> CGImage {
guard let standardImage = Bundle.module.image(forResource: "Brush End") else { ErrorHandler().crash("Unable to load brush end image") }
return try scaledImage(from: standardImage, toHeight: height, color: color)
}

private static func scaledImage(from image: NSImage, toHeight height: CGFloat, color: NSColor) -> NSImage {
private static func scaledImage(from image: NSImage, toHeight height: CGFloat, color: NSColor) throws -> CGImage {
let brushScale = height / image.size.height
let scaledBrushSize = image.size * brushScale

return NSImage(size: scaledBrushSize, flipped: false) { _ -> Bool in
color.setFill()

CGRect(origin: .zero, size: scaledBrushSize).fill()

guard let context = NSGraphicsContext.current?.cgContext else { return false }
context.scaleBy(x: brushScale, y: brushScale)
guard let imageRep = NSBitmapImageRep(
bitmapDataPlanes: nil,
pixelsWide: Int(scaledBrushSize.width),
pixelsHigh: Int(scaledBrushSize.height),
bitsPerSample: 8,
samplesPerPixel: 4,
hasAlpha: true,
isPlanar: false,
colorSpaceName: .deviceRGB,
bytesPerRow: Int(scaledBrushSize.width) * 4,
bitsPerPixel: 32
),
let graphicsContext = NSGraphicsContext(bitmapImageRep: imageRep)
else { throw BrushStampFactoryError.cannotCreateImageContext }
NSGraphicsContext.current = graphicsContext
let context = graphicsContext.cgContext
context.setFillColor(color.cgColor)
context.beginPath()
context.addRect(CGRect(origin: .zero, size: scaledBrushSize))
context.fillPath()
context.scaleBy(x: brushScale, y: brushScale)

image.draw(at: .zero, from: CGRect(origin: .zero, size: image.size), operation: .destinationIn, fraction: 1)
guard let cgImage = context.makeImage() else { throw BrushStampFactoryError.cannotGenerateCGImage(color: color, height: height) }
return cgImage
}

image.draw(at: .zero, from: CGRect(origin: .zero, size: image.size), operation: .destinationIn, fraction: 1)
public static func brushStamp(scaledToHeight height: CGFloat, color: NSColor) throws -> CGImage {
guard let stampImage = Bundle.module.image(forResource: "Brush") else { ErrorHandler().crash("Unable to load brush stamp image") }

return true
}
return try scaledImage(from: stampImage, toHeight: height, color: color)
}
}

enum BrushStampFactoryError: Error {
case cannotCreateImageContext
case cannotGenerateCGImage(color: NSColor, height: CGFloat)
}
#elseif canImport(UIKit)
import ErrorHandling
import Geometry
import UIKit

public class BrushStampFactory: NSObject {
public enum BrushStampFactory {
public static func brushImages(for shape: Shape, color: UIColor, scale: CGFloat) throws -> (CGImage, CGImage) {
let startHeight = shape.topLeft.distance(to: shape.bottomLeft)
let startImage = BrushStampFactory.brushStart(scaledToHeight: startHeight, color: color)
let endHeight = shape.topRight.distance(to: shape.bottomRight)
let endImage = BrushStampFactory.brushEnd(scaledToHeight: endHeight, color: color)
let height = shape.unionDotShapeDotShapeDotUnionCrash.geometryStreamer.height
let startImage = BrushStampFactory.brushStart(scaledToHeight: height, color: color)
let endImage = BrushStampFactory.brushEnd(scaledToHeight: height, color: color)

guard let startCGImage = startImage.cgImage(scale: scale),
let endCGImage = endImage.cgImage(scale: scale)
Expand All @@ -61,7 +92,7 @@ public class BrushStampFactory: NSObject {
}

private static func brushStart(scaledToHeight height: CGFloat, color: UIColor) -> UIImage {
guard let startImage = UIImage(named: "Brush Start", in: Bundle(for: BrushStampFactory.self), compatibleWith: nil)
guard let startImage = UIImage(named: "Brush Start", in: .module, compatibleWith: nil)
else { ErrorHandler().crash("Unable to load brush start image") }

let brushScale = height / startImage.size.height
Expand All @@ -79,7 +110,7 @@ public class BrushStampFactory: NSObject {
}

private static func brushEnd(scaledToHeight height: CGFloat, color: UIColor) -> UIImage {
guard let endImage = UIImage(named: "Brush End", in: Bundle(for: BrushStampFactory.self), compatibleWith: nil)
guard let endImage = UIImage(named: "Brush End", in: .module, compatibleWith: nil)
else { ErrorHandler().crash("Unable to load brush end image") }

let brushScale = height / endImage.size.height
Expand All @@ -96,13 +127,13 @@ public class BrushStampFactory: NSObject {
}
}

public static func brushStamp(scaledToHeight height: CGFloat, color: UIColor) -> UIImage {
public static func brushStamp(scaledToHeight height: CGFloat, color: UIColor) throws -> CGImage {
guard let stampImage = UIImage(named: "Brush") else { ErrorHandler().crash("Unable to load brush stamp image") }

let brushScale = height / stampImage.size.height
let scaledBrushSize = stampImage.size * brushScale

return UIGraphicsImageRenderer(size: scaledBrushSize).image { context in
let scaledBrushImage = UIGraphicsImageRenderer(size: scaledBrushSize).image { context in
color.setFill()
context.fill(CGRect(origin: .zero, size: scaledBrushSize))

Expand All @@ -111,10 +142,17 @@ public class BrushStampFactory: NSObject {

stampImage.draw(at: .zero, blendMode: .destinationIn, alpha: 1)
}

guard let scaledCGImage = scaledBrushImage.cgImage else {
throw BrushStampFactoryError.cannotGenerateStampCGImage(color: color, scale: 1)
}

return scaledCGImage
}
}

enum BrushStampFactoryError: Error {
case cannotGenerateCGImage(shape: Shape, color: UIColor, scale: CGFloat)
case cannotGenerateStampCGImage(color: UIColor, scale: CGFloat)
}
#endif
Loading

0 comments on commit 60be353

Please sign in to comment.