How to get and save GIF Image from NSImageView to disk - swift

Currently I am working on an NSImageView, on which the user drags and drops an image, and the images gets saved to the disk. I am able to save png and jpeg images, but while saving GIF images, all that is saved is a single frame from the gif. The imageView is able to display the whole animated gif though.
My current implementation to save image from NSImageView to disk is:
let cgRef = image.CGImageForProposedRect(nil, context: nil, hints: nil)
let newRep = NSBitmapImageRep(CGImage: cgRef!)
newRep.size = image.size
let type = getBitmapImageFileType(imageName.lowercaseString) // getBitmapImageFileType: returns NSBitmapImageFileType
let properties = type == .NSGIFFileType ? [NSImageLoopCount: 0] : Dictionary<String, AnyObject>()
let data: NSData = newRep.representationUsingType(type, properties: properties)!
data.writeToFile(link, atomically: true)
How should I modify this code to be able to save all the frames of GIF to the disk.

Related

Why does PHImageManager() change the image extension and degrade the image quality when I retrieve images from the photo library?

When I use PHImageManager() to retrieve an image from the photo library, the image is converted from a jpeg image to a png image, and the image is degraded.
How can I retrieve the image without image degradation?
Is it a standard specification that the extension is changed from jpeg to png?
If anyone knows, please respond.
Here is a sample code I made.
img is returned as png.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let assets: PHFetchResult = PHAsset.fetchAssets(with: .image, options: nil)
let mg: PHImageManager = PHImageManager()
guard let asset = assets.lastObject else {
return
}
let options = PHImageRequestOptions()
options.isNetworkAccessAllowed = true
options.deliveryMode = .highQualityFormat
mg.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: options, resultHandler: { image, info in
let img = image
})
}
}
When requesting a high quality image, the result handler of requestImage may be called initially with a low quality version of the image, then later called again with the high quality version.
You can check this by inspecting the PHImageResultIsDegradedKey of the info dictionary parameter of the result handler, for example:
mg.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: options, resultHandler: { image, info in
if info?[PHImageResultIsDegradedKey] as? Bool == true {
print("Degraded image returned, will wait for high quality image")
return
}
let img = image
})
From the documentation for PHImageManager.requestImage:
[...] Photos may call your result handler block more than once. Photos first calls the block to provide a low-quality image suitable for displaying temporarily while it prepares a high-quality image. (If low-quality image data is immediately available, the first call may occur before the method returns.) When the high-quality image is ready, Photos calls your result handler again to provide it. If the image manager has already cached the requested image at full quality, Photos calls your result handler only once. The PHImageResultIsDegradedKey key in the result handler’s info parameter indicates when Photos is providing a temporary low-quality image.

Converting .obj file data to NSImage

I'm trying to convert the data from a .obj file to a base64 string, so that I can display a thumbnail preview of the .obj file in an NSImageView.
Below is my code for getting the asset from a local url, and converting it to a base64 string. I then convert that to Data, which I then attempt to convert to an image.
let fileUrl = Foundation.URL(string: input_string)
let data: NSData? = NSData(contentsOfFile: input_string)
if let fileData = data {
let base64String = fileData.base64EncodedString()
let decodedData = NSData(base64Encoded: base64String, options: NSData.Base64DecodingOptions(rawValue: 0) )
let decodedimage = NSImage(data: decodedData! as Data)
}
I can see that I'm getting the data set, but decodedImage is always nil - I'm guessing this has something to do with .obj image format, which I may not know enough about. Any insight / help appreciated.
EDIT: Here's a screenshot showing what the .obj file looks like in the finder preview.
OBJ is not an image format. Its an old 3D model format. If you want to convert it to an image you will need to open the file and add it to a SCNScene as a geometry node and then render out the the SCNView to an image and save that image.

An image become large in size when it is shared

I am taking an image by device camera and add an autoAdjustmentFilters. Then I compress it's size and save to document directory. Now problem is when I am sharing this image by airdrop, image becomes larger in size. I am facing this problem when I am sharing the image in JPEG format but not in PDF format. Related code is given below:
let aCGImage = inputImage.CGImage;
self.aCIImage = CIImage(CGImage: aCGImage!)
let Filters = self.aCIImage.autoAdjustmentFilters() as [CIFilter]
for Filter in Filters {
Filter.setValue(self.aCIImage, forKey: kCIInputImageKey)
self.outputImage = Filter.outputImage!
}
let CgImage = self.context.createCGImage(self.outputImage, fromRect: self.outputImage.extent)
self.newUIImage = UIImage(CGImage: CgImage!)
let mydata = UIImageJPEGRepresentation(self.newUIImage, 0.5)
print(">>>>>>>>>>>>,,,,,,,,,\((mydata?.length)! / 1024) kb")
Then I save this image data in document directory. When I save this image in document directory, it's size is 600kb-1500kb, but after sharing it's size is larger than 10000kb. Don't understand what the problem is.

Making a GIF from images using Swift (macOS)

I was wondering if there was a way to convert an array of NSImages using Swift in macOS/osx?
I should be able to export it to file afterwards, so an animation of images displayed on my app would not be enough.
Thanks!
Image I/O has the functionalities you need. Try this:
var images = ... // init your array of NSImage
let destinationURL = NSURL(fileURLWithPath: "/path/to/image.gif")
let destinationGIF = CGImageDestinationCreateWithURL(destinationURL, kUTTypeGIF, images.count, nil)!
// The final size of your GIF. This is an optional parameter
var rect = NSMakeRect(0, 0, 350, 250)
// This dictionary controls the delay between frames
// If you don't specify this, CGImage will apply a default delay
let properties = [
(kCGImagePropertyGIFDictionary as String): [(kCGImagePropertyGIFDelayTime as String): 1.0/16.0]
]
for img in images {
// Convert an NSImage to CGImage, fitting within the specified rect
// You can replace `&rect` with nil
let cgImage = img.CGImageForProposedRect(&rect, context: nil, hints: nil)!
// Add the frame to the GIF image
// You can replace `properties` with nil
CGImageDestinationAddImage(destinationGIF, cgImage, properties)
}
// Write the GIF file to disk
CGImageDestinationFinalize(destinationGIF)

AppleTv / TvOS - Resize TopShelf Images in TVContentItems Saved to ImageURL

I am implementing TopShelf in Apple tvOS. I have downloaded images and assigned to imageURL of TVContentItems. The downloaded images aspect ratio does not fit into the TopShelf Images properly. I have tried to change the size by appending width + height to the image link.
www.mydownloadedimages.com/{width}x{height}
But it didn't work.
Can I do resizing at client end in any other way. In the TVContentItem class I have only have NSURL object. There is no UIImage Object.
Thanks a lot.
Here is Apple's documentation on Image Sizes and shapes
// ~ 2 : 3
// ~ 1 : 1
// ~ 4 : 3
// ~ 16 : 9
// ~ 8 : 3
// ~ 87 : 28
//#property imageShape
//#abstract A TVContentItemImageShape value describing the intended aspect ratio or shape of the image.
//#discussion For Top Shelf purposes: the subset of values which are //valid in this property, for TVContentItems in the topShelfItems property //of the TVTopShelfProvider, depends on the value of the topShelfStyle //property of the TVTopShelfProvider:
TVTopShelfContentStyleInset:
valid: TVContentItemImageShapeExtraWide
TVTopShelfContentStyleSectioned:
valid: TVContentItemImageShapePoster
valid: TVContentItemImageShapeSquare
valid: TVContentItemImageShapeHDTV
When the value of this property is not valid for the Top Shelf style, the system reserves the right to scale the image in any way.
You're right saying that the TVContentItem has no UIImage type property. Since TVContentItem also accepts local file URLs in the imageURL property, a workaround can be:
grabbing the UIImage from internet
creating a new image context with the size of the top shelf image
saving it into the NSCacheDirectory
setting the local image URL as imageURL.
Here are the steps:
Let's create our TVContentItem object:
let identifier = TVContentIdentifier(identifier: "myPicture", container: wrapperID)!
let contentItem = TVContentItem(contentIdentifier: identifier )!
Set the contentItem's imageShape:
contentItem.imageShape = .HDTV
Grab the image from Internet. Actually I did this synchronously, you can also try to use other async methods to get that (NSURLConnection, AFNetworking, etc...):
let data : NSData = NSData(contentsOfURL: NSURL(string: "https://s3-ak.buzzfed.com/static/2014-07/16/9/enhanced/webdr08/edit-14118-1405517808-7.jpg")!)!
Prepare the path where your image will be saved and get your UIImage from the data object:
let filename = "picture-test.jpg"
let paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)
let filepath = paths.first! + "/" + filename
let img : UIImage = UIImage(data: data)!
Assuming you've already set the topShelfStyle property, get the size of the top shelf image with the method TVTopShelfImageSizeForShape. This will be the size of your image context:
let shapeSize : CGSize = TVTopShelfImageSizeForShape(contentItem.imageShape, self.topShelfStyle)
Create your image context of shapeSize size and draw the downloaded image into the context rect. Here you can do all your modifications to the image to adjust it into the desired size. In this example I took a square image from Instagram and I put white letterbox bands on the right and left sides.
UIGraphicsBeginImageContext(shapeSize)
let imageShapeInRect : CGRect = CGRectMake((shapeSize.width-shapeSize.height)/2,0,shapeSize.height,shapeSize.height)
img.drawInRect(imageShapeInRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
In the end, save this image into your NSCacheDirectory and set the image path as contentItem's imageURL.
UIImageJPEGRepresentation(newImage, 0.8)!.writeToFile(filepath, atomically: true)
contentItem.imageURL = NSURL(fileURLWithPath: filepath)
Complete your TVContentItem with other details (like title, internal link, etc...), run the top shelf extension... et voilĂ !
I would like to add to Nicola Giancecchi's answer. The Top Shelf extension will run on a non main thread so using methods like UIImageJPEGRepresentation or UIImagePNGRepresentation will sometimes stop the Top Shelf thread printing this in the console:
Program ended with exit code: 0
To fix this you can wrap your code like this:
DispatchQueue.main.sync {
UIImageJPEGRepresentation(newImage, 0.8)!.writeToFile(filepath, atomically: true)
let imageURL = NSURL(fileURLWithPath: filepath)
if #available(tvOSApplicationExtension 11.0, *) {
contentItem.setImageURL(imageURL, forTraits: .userInterfaceStyleLight)
contentItem.setImageURL(imageURL, forTraits: .userInterfaceStyleDark)
} else {
contentItem.imageURL = imageURL
}
}