Rotate Image in Share Extension

Rotate image in share extension

First of all it is clear that you have a memory crash. According to App Extension Programming Guide:

Memory limits for running app extensions are significantly lower than the memory limits imposed on a foreground app. On both platforms, the system may aggressively terminate extensions because users want to return to their main goal in the host app.

And from error it is clear that you exceed 120 mb. But you might wonder what is took so much memory.

According to Optimizing Images
Written by Jordan Morgan:

iOS essentially derives its memory hit from an image’s dimensions - whereas the actual file size has much less to do with it.

So if we calculate size or 4032 x 3024 photo it will be... 46mb for 4 bit color and 79mb for 8 bit color. Pretty big, but still less that a limit...

Thing is - you have two copies of your image. One is original and second one - rotated.

To solve this issue you need load only rotated image into memory, without original. This can be done with Image I/O Framework:

extension UIImage {
static func imageWithFixedOrientation(at url: URL) -> UIImage? {
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else { return nil }

guard let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? Dictionary<CFString, Any> else { return nil }

guard
let width = imageProperties[kCGImagePropertyPixelWidth] as? CGFloat,
let height = imageProperties[kCGImagePropertyPixelHeight] as? CGFloat
else { return nil }

let options: [NSString: Any] = [
kCGImageSourceThumbnailMaxPixelSize: max(width, height),
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true
]

guard let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { return nil }

return UIImage(cgImage: cgImage)
}
}

In sample app:

extension ViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let url = info[.imageURL] as? URL else { return }

let image = UIImage.imageWithFixedOrientation(at: url)
}
}

it reduced memory peaks from 180+mb to just 80mb.

Rotate Selected Image with jQueryRotate In chrome extension

i found some old extension files from the extension IMG Rotate, and update them because they use precarious methods. but they dont use jQueryRotate.

Manifest.json

{
"manifest_version": 2,
"name": "Rotar ",
"version": "1.0",
"background": {
"page": "background.html"
},
"permissions" : [
"contextMenus",
"tabs",
"http://*/*",
"https://*/*"
],
"content_scripts" : [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["jquery-1.5.2.min","contentscript.js"]
}
],
"icons" : {
"16" : "icon-bitty.png",
"48" : "icon-small.png",
"128" : "icon-large.png"
}
}

background.html

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="jquery-1.5.2.min"></script>
<script type="text/javascript" src="contentscript.js"></script>
<script type="text/javascript" src="background.js"></script>
</head>
<body>
</body>
</html>

background.js

        /**
* Returns a handler which will open a new window when activated.
*/
function rotateClockWise() {
return function (info, tab1) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {angle: 90, rotation: 'rotate'}, function(response) {

});
});
};
}

function rotateCounterClockWise() {
return function (info, tab1) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {angle: -90, rotation: 'rotate'}, function(response) {

});
});
};
}

function upright() {
return function (info, tab1) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {angle: 0, rotation: 'flip'}, function(response) {

});
});
};
}

function upsideDown() {
return function (info, tab1) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {angle: 180, rotation: 'flip'}, function(response) {

});
});
};
}

/**
* Create a context menu which will only show up for images.
*/
chrome.contextMenus.create({
"title" : "Rotar 90",
"type" : "normal",
"contexts" : ["all"],
"onclick" : rotateClockWise()
});

chrome.contextMenus.create({
"title" : "Rotar -90",
"type" : "normal",
"contexts" : ["all"],
"onclick" : rotateCounterClockWise()
});

chrome.contextMenus.create({
"title" : "Rotar 180",
"type" : "normal",
"contexts" : ["all"],
"onclick" : upsideDown()
});

chrome.contextMenus.create({
"title" : "Rotar -180",
"type" : "normal",
"contexts" : ["all"],
"onclick" : upright()
});

contentscript.js

var selectedImage = null;
$("*", document.body).mousedown(function (event) {
switch (event.which) {
case 3:
selectedImage = $(this).get(0);
event.stopPropagation();
break;
default:
break;
}
});

chrome.extension.onRequest.addListener(onRequest);

function onRequest(request, sender, sendResponse) {
var degree;
if (request.rotation == 'flip') {
degree = request.angle;
} else {
degree = getCurrentRotation(request.angle);
}
selectedImage.style.webkitTransform = 'rotate(' + degree + 'deg)';
sendResponse({}); // clean up.
}

function getCurrentRotation(angle) {
var currentDegree = selectedImage.style['-webkit-transform'];
if (currentDegree && currentDegree != "") {
var start = currentDegree.indexOf('(');
var end = currentDegree.indexOf('deg)');
angle = parseInt(currentDegree.substring(start + 1, end)) + angle;
}
return angle;
}

And you also need jquery to make this work
https://code.jquery.com/jquery-1.5.2.min.js

Swift: Rotating UIImage and filling it

You just need to set a fill color and fill your context before rotating the image:



extension UIImage {
func rotatedBy(degree: CGFloat) -> UIImage? {
guard let cgImage = cgImage else { return nil }
UIGraphicsBeginImageContextWithOptions(size, false, 0)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
defer { UIGraphicsEndImageContext() }
UIColor.white.setFill()
context.fill(.init(origin: .zero, size: size))
context.translateBy(x: size.width/2, y: size.height/2)
context.scaleBy(x: 1, y: -1)
context.rotate(by: -degree * .pi / 180)
context.draw(cgImage, in: CGRect(origin: .init(x: -size.width/2, y: -size.height/2), size: size))
return UIGraphicsGetImageFromCurrentImageContext()
}
}

How to rotate image in Swift?

This is an extension of UIImage that targets Swift 4.0 and can rotate just the image without the need for a UIImageView. Tested successfully that the image was rotated, and not just had its exif data changed.

import UIKit

extension UIImage {
func rotate(radians: CGFloat) -> UIImage {
let rotatedSize = CGRect(origin: .zero, size: size)
.applying(CGAffineTransform(rotationAngle: CGFloat(radians)))
.integral.size
UIGraphicsBeginImageContext(rotatedSize)
if let context = UIGraphicsGetCurrentContext() {
let origin = CGPoint(x: rotatedSize.width / 2.0,
y: rotatedSize.height / 2.0)
context.translateBy(x: origin.x, y: origin.y)
context.rotate(by: radians)
draw(in: CGRect(x: -origin.y, y: -origin.x,
width: size.width, height: size.height))
let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

return rotatedImage ?? self
}

return self
}
}

To perform a 180 degree rotation, you can call it like this:

let rotatedImage = image.rotate(radians: .pi)

If for whatever reason it fails to rotate, the original image will then be returned.

Rotate a UIImage without memory spikes

I was able to (mostly) solve this by calling UIGraphicsEndImageContext() at the end of the above code, as well as not rotating the image until just before it is uploaded (users can rotate their photos before uploading them, so not rotating them until just before upload greatly decreases the memory usage).

So, whenever the user rotates a photo, I call this rotateExif(orientation) function that I created

extension UIImage{
func rotateExif(orientation: UIImageOrientation) -> UIImage{

if(orientation == UIImageOrientation.Up){return self}

let current = self.imageOrientation
let currentDegrees: Int = (
current == UIImageOrientation.Down || current == UIImageOrientation.DownMirrored ? 180 : (
current == UIImageOrientation.Left || current == UIImageOrientation.LeftMirrored ? 270 : (
current == UIImageOrientation.Right || current == UIImageOrientation.RightMirrored ? 90 : 0
)
)
)
let changeDegrees: Int = (
orientation == UIImageOrientation.Down || orientation == UIImageOrientation.DownMirrored ? 180 : (
orientation == UIImageOrientation.Left || orientation == UIImageOrientation.LeftMirrored ? 270 : (
orientation == UIImageOrientation.Right || orientation == UIImageOrientation.RightMirrored ? 90 : 0
)
)
)

let mirrored: Bool = (
current == UIImageOrientation.DownMirrored || current == UIImageOrientation.UpMirrored ||
current == UIImageOrientation.LeftMirrored || current == UIImageOrientation.RightMirrored ||
orientation == UIImageOrientation.DownMirrored || orientation == UIImageOrientation.UpMirrored ||
orientation == UIImageOrientation.LeftMirrored || orientation == UIImageOrientation.RightMirrored
)

let degrees: Int = currentDegrees + changeDegrees

let newOrientation: UIImageOrientation = (
degrees == 270 || degrees == 630 ? (mirrored ? UIImageOrientation.LeftMirrored : UIImageOrientation.Left) : (
degrees == 180 || degrees == 540 ? (mirrored ? UIImageOrientation.DownMirrored : UIImageOrientation.Down) : (
degrees == 90 || degrees == 450 ? (mirrored ? UIImageOrientation.RightMirrored : UIImageOrientation.Right) : (
mirrored ? UIImageOrientation.UpMirrored : UIImageOrientation.Up
)
)
)
)

return UIImage(CGImage: self.CGImage!, scale: 1.0, orientation: newOrientation)
}
}

This rotates the image's EXIF by the amount that is inputted in orientation. So, for example, if the original orientation was Right, and it was rotated Right, the new orientation would be Down. If the original was RightMirrored, and it was rotated Left, the new orientation would be UpMirrored.

Then, before uploading the photo, the actual pixels are rotated by calling the code in the question as an extension on UIImage, image.rotate(image.imageOrientation).

Photos Extension: Can't Save Images Not Oriented Up

I have certainly seen the save fail because the orientation stuff was wrong, but the following architecture currently seems to work for me:

func startContentEditing(with contentEditingInput: PHContentEditingInput, placeholderImage: UIImage) {
self.input = contentEditingInput
if let im = self.input?.displaySizeImage {
self.displayImage = CIImage(image:im, options: [.applyOrientationProperty:true])!
// ... other stuff depending on what the adjustment data was ...
}
self.mtkview.setNeedsDisplay()
}
func finishContentEditing(completionHandler: @escaping ((PHContentEditingOutput?) -> Void)) {
DispatchQueue.global(qos:.default).async {
let inurl = self.input!.fullSizeImageURL!
let output = PHContentEditingOutput(contentEditingInput:self.input!)
let outurl = output.renderedContentURL
var ci = CIImage(contentsOf: inurl, options: [.applyOrientationProperty:true])!
let space = ci.colorSpace!
// ... apply real filter to `ci` based on user edits ...
try! CIContext().writeJPEGRepresentation(
of: ci, to: outurl, colorSpace: space)
let data = // whatever
output.adjustmentData = PHAdjustmentData(
formatIdentifier: self.myidentifier, formatVersion: "1.0", data: data)
completionHandler(output)
}
}


Related Topics



Leave a reply



Submit