@@ -10,18 +10,40 @@ import UIKit
1010import Cocoa
1111#endif
1212
13+ public enum PictureInputError : Error , CustomStringConvertible {
14+ case zeroSizedImageError
15+ case dataProviderNilError
16+ case noSuchImageError( imageName: String )
17+
18+ public var errorDescription : String {
19+ switch self {
20+ case . zeroSizedImageError:
21+ return " Tried to pass in a zero-sized image "
22+ case . dataProviderNilError:
23+ return " Unable to retrieve image dataProvider "
24+ case . noSuchImageError( let imageName) :
25+ return " No such image named: \( imageName) in your application bundle "
26+ }
27+ }
28+
29+ public var description : String {
30+ return " < \( type ( of: self ) ) : errorDescription = \( self . errorDescription) > "
31+ }
32+ }
33+
1334public class PictureInput : ImageSource {
1435 public let targets = TargetContainer ( )
15- var imageFramebuffer : Framebuffer !
36+ var imageFramebuffer : Framebuffer ?
37+ public var framebufferUserInfo : [ AnyHashable : Any ] ?
1638 var hasProcessedImage : Bool = false
1739
18- public init ( image: CGImage , smoothlyScaleOutput: Bool = false , orientation: ImageOrientation = . portrait) {
40+ public init ( image: CGImage , smoothlyScaleOutput: Bool = false , orientation: ImageOrientation = . portrait) throws {
1941 // TODO: Dispatch this whole thing asynchronously to move image loading off main thread
2042 let widthOfImage = GLint ( image. width)
2143 let heightOfImage = GLint ( image. height)
2244
2345 // If passed an empty image reference, CGContextDrawImage will fail in future versions of the SDK.
24- guard ( ( widthOfImage > 0 ) && ( heightOfImage > 0 ) ) else { fatalError ( " Tried to pass in a zero-sized image " ) }
46+ guard ( ( widthOfImage > 0 ) && ( heightOfImage > 0 ) ) else { throw PictureInputError . zeroSizedImageError }
2547
2648 var widthToUseForTexture = widthOfImage
2749 var heightToUseForTexture = heightOfImage
@@ -67,12 +89,12 @@ public class PictureInput: ImageSource {
6789 if ( bitmapInfo. contains ( . byteOrder32Little) ) {
6890 /* Little endian, for alpha-first we can use this bitmap directly in GL */
6991 if ( ( alphaInfo != CGImageAlphaInfo . premultipliedFirst) && ( alphaInfo != CGImageAlphaInfo . first) && ( alphaInfo != CGImageAlphaInfo . noneSkipFirst) ) {
70- shouldRedrawUsingCoreGraphics = true
92+ shouldRedrawUsingCoreGraphics = true
7193 }
7294 } else if ( ( bitmapInfo. contains ( CGBitmapInfo ( ) ) ) || ( bitmapInfo. contains ( . byteOrder32Big) ) ) {
7395 /* Big endian, for alpha-last we can use this bitmap directly in GL */
7496 if ( ( alphaInfo != CGImageAlphaInfo . premultipliedLast) && ( alphaInfo != CGImageAlphaInfo . last) && ( alphaInfo != CGImageAlphaInfo . noneSkipLast) ) {
75- shouldRedrawUsingCoreGraphics = true
97+ shouldRedrawUsingCoreGraphics = true
7698 } else {
7799 /* Can access directly using GL_RGBA pixel format */
78100 format = GL_RGBA
@@ -82,36 +104,30 @@ public class PictureInput: ImageSource {
82104 }
83105 }
84106
85- // CFAbsoluteTime elapsedTime, startTime = CFAbsoluteTimeGetCurrent();
86-
87- if ( shouldRedrawUsingCoreGraphics) {
88- // For resized or incompatible image: redraw
89- imageData = UnsafeMutablePointer< GLubyte> . allocate( capacity: Int ( widthToUseForTexture * heightToUseForTexture) * 4 )
90-
91- let genericRGBColorspace = CGColorSpaceCreateDeviceRGB ( )
107+ try sharedImageProcessingContext. runOperationSynchronously {
108+ // CFAbsoluteTime elapsedTime, startTime = CFAbsoluteTimeGetCurrent();
92109
93- let imageContext = CGContext ( data: imageData, width: Int ( widthToUseForTexture) , height: Int ( heightToUseForTexture) , bitsPerComponent: 8 , bytesPerRow: Int ( widthToUseForTexture) * 4 , space: genericRGBColorspace, bitmapInfo: CGImageAlphaInfo . premultipliedFirst. rawValue | CGBitmapInfo . byteOrder32Little. rawValue)
94- // CGContextSetBlendMode(imageContext, kCGBlendModeCopy); // From Technical Q&A QA1708: http://developer.apple.com/library/ios/#qa/qa1708/_index.html
95- imageContext? . draw ( image, in: CGRect ( x: 0.0 , y: 0.0 , width: CGFloat ( widthToUseForTexture) , height: CGFloat ( heightToUseForTexture) ) )
96- } else {
97- // Access the raw image bytes directly
98- dataFromImageDataProvider = image. dataProvider? . data
99- #if os(iOS)
100- imageData = UnsafeMutablePointer < GLubyte > ( mutating: CFDataGetBytePtr ( dataFromImageDataProvider) )
101- #else
102- imageData = UnsafeMutablePointer < GLubyte > ( mutating: CFDataGetBytePtr ( dataFromImageDataProvider) !)
103- #endif
104- }
105-
106- sharedImageProcessingContext. runOperationSynchronously {
107- do {
108- // TODO: Alter orientation based on metadata from photo
109- self . imageFramebuffer = try Framebuffer ( context: sharedImageProcessingContext, orientation: orientation, size: GLSize ( width: widthToUseForTexture, height: heightToUseForTexture) , textureOnly: true )
110- } catch {
111- fatalError ( " ERROR: Unable to initialize framebuffer of size ( \( widthToUseForTexture) , \( heightToUseForTexture) ) with error: \( error) " )
110+ if ( shouldRedrawUsingCoreGraphics) {
111+ // For resized or incompatible image: redraw
112+ imageData = UnsafeMutablePointer< GLubyte> . allocate( capacity: Int ( widthToUseForTexture * heightToUseForTexture) * 4 )
113+
114+ let genericRGBColorspace = CGColorSpaceCreateDeviceRGB ( )
115+
116+ let imageContext = CGContext ( data: imageData, width: Int ( widthToUseForTexture) , height: Int ( heightToUseForTexture) , bitsPerComponent: 8 , bytesPerRow: Int ( widthToUseForTexture) * 4 , space: genericRGBColorspace, bitmapInfo: CGImageAlphaInfo . premultipliedFirst. rawValue | CGBitmapInfo . byteOrder32Little. rawValue)
117+ // CGContextSetBlendMode(imageContext, kCGBlendModeCopy); // From Technical Q&A QA1708: http://developer.apple.com/library/ios/#qa/qa1708/_index.html
118+ imageContext? . draw ( image, in: CGRect ( x: 0.0 , y: 0.0 , width: CGFloat ( widthToUseForTexture) , height: CGFloat ( heightToUseForTexture) ) )
119+ } else {
120+ // Access the raw image bytes directly
121+ guard let data = image. dataProvider? . data else { throw PictureInputError . dataProviderNilError }
122+ dataFromImageDataProvider = data
123+ imageData = UnsafeMutablePointer < GLubyte > ( mutating: CFDataGetBytePtr ( dataFromImageDataProvider) )
112124 }
113-
114- glBindTexture ( GLenum ( GL_TEXTURE_2D) , self . imageFramebuffer. texture)
125+
126+ // TODO: Alter orientation based on metadata from photo
127+ self . imageFramebuffer = try Framebuffer ( context: sharedImageProcessingContext, orientation: orientation, size: GLSize ( width: widthToUseForTexture, height: heightToUseForTexture) , textureOnly: true )
128+ self . imageFramebuffer!. lock ( )
129+
130+ glBindTexture ( GLenum ( GL_TEXTURE_2D) , self . imageFramebuffer!. texture)
115131 if ( smoothlyScaleOutput) {
116132 glTexParameteri ( GLenum ( GL_TEXTURE_2D) , GLenum ( GL_TEXTURE_MIN_FILTER) , GL_LINEAR_MIPMAP_LINEAR)
117133 }
@@ -130,45 +146,53 @@ public class PictureInput: ImageSource {
130146 }
131147
132148#if canImport(UIKit)
133- public convenience init ( image: UIImage , smoothlyScaleOutput: Bool = false , orientation: ImageOrientation = . portrait) {
134- self . init ( image: image. cgImage!, smoothlyScaleOutput: smoothlyScaleOutput, orientation: orientation)
149+ public convenience init ( image: UIImage , smoothlyScaleOutput: Bool = false , orientation: ImageOrientation = . portrait) throws {
150+ try self . init ( image: image. cgImage!, smoothlyScaleOutput: smoothlyScaleOutput, orientation: orientation)
135151 }
136152#else
137153 public convenience init ( image: NSImage , smoothlyScaleOutput: Bool = false , orientation: ImageOrientation = . portrait) {
138- self . init ( image: image. cgImage ( forProposedRect: nil , context: nil , hints: nil ) !, smoothlyScaleOutput: smoothlyScaleOutput, orientation: orientation)
154+ try self . init ( image: image. cgImage ( forProposedRect: nil , context: nil , hints: nil ) !, smoothlyScaleOutput: smoothlyScaleOutput, orientation: orientation)
139155 }
140156#endif
141157
142- public convenience init ( imageName: String , smoothlyScaleOutput: Bool = false , orientation: ImageOrientation = . portrait) {
158+ public convenience init ( imageName: String , smoothlyScaleOutput: Bool = false , orientation: ImageOrientation = . portrait) throws {
143159#if canImport(UIKit)
144- guard let image = UIImage ( named: imageName) else { fatalError ( " No such image named: \ ( imageName) in your application bundle " ) }
145- self . init ( image: image. cgImage!, smoothlyScaleOutput: smoothlyScaleOutput, orientation: orientation)
160+ guard let image = UIImage ( named: imageName) else { throw PictureInputError . noSuchImageError ( imageName: imageName ) }
161+ try self . init ( image: image. cgImage!, smoothlyScaleOutput: smoothlyScaleOutput, orientation: orientation)
146162#else
147- guard let image = NSImage ( named: NSImage . Name ( imageName) ) else { fatalError ( " No such image named: \ ( imageName) in your application bundle " ) }
148- self . init ( image: image. cgImage ( forProposedRect: nil , context: nil , hints: nil ) !, smoothlyScaleOutput: smoothlyScaleOutput, orientation: orientation)
163+ guard let image = NSImage ( named: NSImage . Name ( imageName) ) else { throw PictureInputError . noSuchImageError ( imageName: imageName ) }
164+ try self . init ( image: image. cgImage ( forProposedRect: nil , context: nil , hints: nil ) !, smoothlyScaleOutput: smoothlyScaleOutput, orientation: orientation)
149165#endif
150166 }
151167
168+ deinit {
169+ self . imageFramebuffer? . unlock ( )
170+ }
171+
152172 public func processImage( synchronously: Bool = false ) {
153173 if synchronously {
154- sharedImageProcessingContext. runOperationSynchronously {
155- sharedImageProcessingContext. makeCurrentContext ( )
156- self . updateTargetsWithFramebuffer ( self . imageFramebuffer)
157- self . hasProcessedImage = true
174+ sharedImageProcessingContext. runOperationSynchronously {
175+ if let framebuffer = self . imageFramebuffer {
176+ sharedImageProcessingContext. makeCurrentContext ( )
177+ self . updateTargetsWithFramebuffer ( framebuffer)
178+ self . hasProcessedImage = true
179+ }
158180 }
159181 } else {
160- sharedImageProcessingContext. runOperationAsynchronously {
161- sharedImageProcessingContext. makeCurrentContext ( )
162- self . updateTargetsWithFramebuffer ( self . imageFramebuffer)
163- self . hasProcessedImage = true
182+ sharedImageProcessingContext. runOperationAsynchronously {
183+ if let framebuffer = self . imageFramebuffer {
184+ sharedImageProcessingContext. makeCurrentContext ( )
185+ self . updateTargetsWithFramebuffer ( framebuffer)
186+ self . hasProcessedImage = true
187+ }
164188 }
165189 }
166190 }
167191
168192 public func transmitPreviousImage( to target: ImageConsumer , atIndex: UInt ) {
169- if hasProcessedImage {
170- imageFramebuffer . lock ( )
171- target. newFramebufferAvailable ( imageFramebuffer , fromSourceIndex: atIndex)
193+ if let framebuffer = self . imageFramebuffer , hasProcessedImage {
194+ framebuffer . lock ( )
195+ target. newFramebufferAvailable ( framebuffer , fromSourceIndex: atIndex)
172196 }
173197 }
174198}
0 commit comments