How do I change the pixelFormat in Metal? - macos

If I try anyting other than bgra8Unorm, it will crash, saying,
-[MTLDebugRenderCommandEncoder validateFramebufferWithRenderPipelineState:]:1192: failed assertion `For color attachment 0, the render pipeline's pixelFormat (MTLPixelFormatBGRA8Unorm_sRGB) does not match the framebuffer's pixelFormat (MTLPixelFormatBGRA8Unorm).'
How do I change the framebuffer's pixelFormat then? I want to be able to do this:
PipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm_srgb
Instead of this
PipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
Again, this is an attempt to fix this stupid bug.

You need the MetalView defined here:
var MetalView: MTKView {
return view as! MTKView
}
Then in viewDidLoad:
MetalView.colorPixelFormat = .bgra8Unorm_srgb
Then I can do this:
PipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm_srgb
I figured it out myself yay!

Related

Render off-screen WKWebView into NSImage

I've tried rendering an offscreen WKWebView into an image using
func cacheDisplayInRect(rect: NSRect, toBitmapImageRep bitmapImageRep: NSBitmapImageRep)
and func drawLayer(layer: CALayer, inContext ctx: CGContext)
without success. The resulting image is always empty (white or transparent). Has anyone managed to do this on Yosemite?
You can do it using drawViewHierarchyInRect: none of the other methods seem to work, use it like so:
UIGraphicsBeginImageContextWithOptions(newRect.size, YES, 0);
BOOL ok = [view drawViewHierarchyInRect:newRect afterScreenUpdates:YES];
if (!ok) {
NSLog(#"Problem with drawView...");
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I'm doing the same in iOS, but unfortunately this method is slow, must also be run in the main thread and only works if afterScreenUpdates is set to yes. See this answer : How can I take a snapshot of a UIView that isn't rendered?
Also there's no way to tell, from what I can see, if any aspect of the webpage needs redrawing.

image cropping an AVCaptureSession Image

So I have been at it all day to no luck and it has been needless to say quite frustrating, I have looked up many examples and downloadable categories which all tout being able to crop images flawlessly. Which they do, However the minute i try to do it from an image genrated via AVCaptureSession it does not work as well. I consulted both these sources
http://codefuel.wordpress.com/2011/04/22/image-cropping-from-a-uiscrollview/
http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
and the project from the first link seems to work directly as advertised but as soon as i hack it to do the same magic on an av capture image...nope...
does anyone have insight into this? Also here is my code for reference.
- (IBAction)TakePhotoPressed:(id)sender
{
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections)
{
for (AVCaptureInputPort *port in [connection inputPorts])
{
if ([[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
//NSLog(#"about to request a capture from: %#", stillImageOutput);
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
CFDictionaryRef exifAttachments = CMGetAttachment( imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
if (exifAttachments)
{
// Do something with the attachments.
//NSLog(#"attachements: %#", exifAttachments);
}
else
NSLog(#"no attachments");
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
NSLog(#"%f",image.size.width);
NSLog(#"%f",image.size.height);
float scale = 1.0f/_scrollView.zoomScale;
CGRect visibleRect;
visibleRect.origin.x = _scrollView.contentOffset.x * scale;
visibleRect.origin.y = _scrollView.contentOffset.x * scale;
visibleRect.size.width = _scrollView.bounds.size.width * scale;
visibleRect.size.height = _scrollView.bounds.size.height * scale;
UIImage* cropped = [self cropImage:image withRect:visibleRect];
[croppedImage setImage:cropped];
[image release];
}
];
[croppedImage setHidden:NO];
}
cropImage function used above.
-(UIImage*)cropImage :(UIImage*)originalImage withRect :(CGRect) rect
{
CGRect transformedRect=rect;
if(originalImage.imageOrientation==UIImageOrientationRight)
{
transformedRect.origin.x = rect.origin.y;
transformedRect.origin.y = originalImage.size.width-(rect.origin.x+rect.size.width);
transformedRect.size.width = rect.size.height;
transformedRect.size.height = rect.size.width;
}
CGImageRef cr = CGImageCreateWithImageInRect(originalImage.CGImage, transformedRect);
UIImage* cropped = [UIImage imageWithCGImage:cr scale:originalImage.scale orientation:originalImage.imageOrientation];
[croppedImage setFrame:CGRectMake(croppedImage.frame.origin.x,
croppedImage.frame.origin.y,
cropped.size.width,
cropped.size.height)];
CGImageRelease(cr);
return cropped;
}
I am also tempted for verbosity and arming whomever might help me in my plight with as much information as possible to post my init of my scrollView and avcapture session. However That may be a bit too much so if you want to see it just ask.
Now as for results of what the code actually does?..
What it looks like before i take the picture
And After...
EDIT:
Well I have a few views now and no comment's so either no one has figured it out or it's so simple they thought i would have figured it out again...In any case i have not made any progress. So for anyone interested here is a small sample app with the code all set up and you can see what i am doing
https://docs.google.com/open?id=0Bxr4V3a9QFM_NnoxMkhzZTVNVEE
It seems that this little conundrum did not only have me stumped as after nearly a week,but a scant few of whoever viewed my question had no suggestions either. I must say for this particular problem i could not get it to work in this way, I pondered and tinkered and mused for a while to no avail. Until i did this
[self HideElements];
UIGraphicsBeginImageContext(chosenPhotoView.frame.size);
[chosenPhotoView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self ShowElements];
And that's it, less code and it worked pretty much instantly. So instead of trying to crop an image via the scrollview I take a screenshot of the screen at that time then crop the image using the scrollviews frame variables. And the hide/show element functions hide any overlapping elements on the picture i want.

Taking a screenshot of MKMapView

I'm trying to get a screenshot of a MKMapView.
and I'm using the following code:
UIGraphicsBeginImageContext(myMapView.frame.size);
[myMapView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenShot=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenShot;
And I'm getting and almost blank image with the map current location icon and a Google logo in it.
What could be causing that?
I should tell you that myMapView is actually on another viewController's view but since I'm getting the blue spot showing the location and the google logo I assume the reference I have is the correct one.
Thank you.
iOS 7 introduced a new method to generate screenshots of a MKMapView. It is now possible to use the new MKMapSnapshot API as follows:
MKMapView *mapView = [..your mapview..]
MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc]init];
options.region = mapView.region;
options.mapType = MKMapTypeStandard;
options.showsBuildings = NO;
options.showsPointsOfInterest = NO;
options.size = CGSizeMake(1000, 500);
MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc]initWithOptions:options];
[snapshotter startWithQueue:dispatch_get_main_queue() completionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
if( error ) {
NSLog( #"An error occurred: %#", error );
} else {
[UIImagePNGRepresentation( snapshot.image ) writeToFile:#"/Users/<yourAccountName>/map.png" atomically:YES];
}
}];
Currently all overlays and annotations are not rendered. You have to render them afterwards onto the resulting snapshot image yourself. The provided MKMapSnapshot object has a handy helper method to do the mapping between coordinates and points:
CGPoint point = [snapshot pointForCoordinate:locationCoordinate2D];
As mentioned here, you can try this
- (UIImage*) renderToImage
{
UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

UITextField secureTextEntry - works going from YES to NO, but changing back to YES has no effect

The above says it all- I have a UITextField set to secure, but want to give users the option to make it not secure (so they can see for sure what they typed if they are in a private area). However, suppose they hit the toggle by mistake, and want to change it back to secure mode? That does not work. I've tried everything - using -1 instead of YES, deleting the text and then putting it back. I'm at a total loss on other ideas. [Entered rdar://9781908]
EDIT: I believe this issue is fixed as of iOS5.
It must be input-focus issue: when focused, UITextField can change only ON->OFF.
Try next trick to switch OFF->ON:
textField.enabled = NO;
textField.secureTextEntry = YES;
textField.enabled = YES;
[textField becomeFirstResponder];
EDIT (dhoerl): In iOS 9.3, I found this works but there is a problem. If you enter say 3 characters in plain view, then switch to secure text, then type a character, the 3 pre-existing characters disappear. I tried all kinds of trick to clear, then reset the text without success. I finally tried just playing with the control by cutting and pasting - pasting into the newly-switched-to-secure-mode worked great. So I emulated it in code, and now it all works fine - no need to play with the responder either. Here is what I finally ended up with:
if textview should go into secure mode and the textfield is not empty {
textField.text = ""
textField.secureTextEntry = true
UIPasteboard.generalPasteboard().string = password
textField.paste(self)
UIPasteboard.generalPasteboard().string = ""
}
I'd prefer to not have to do this paste, but if you want it seamless this is the only way I can find to do it.
Mike R's solution is nice, but I prefer this approach:
BOOL wasFirstResponder;
if ((wasFirstResponder = [passwordField isFirstResponder])) {
[passwordField resignFirstResponder];
}
// In this example, there is a "show password" toggle
[passwordField setSecureTextEntry:![passwordField isSecureTextEntry]];
if (wasFirstResponder) {
[passwordField becomeFirstResponder];
}
That way you only becomeFirstResponder again when necessary.
I entered rdar against this problem, but did find a work around. Essentially you have to programmatically replace the "stuck" control with a new one. The easiest thing to do is to archive the existing control in viewDidLoad then unarchive as needed:
// do in viewDidLoad
self.passwordMemberArchive = [NSMutableData data];
NSKeyedArchiver *ka = [[NSKeyedArchiver alloc] initForWritingWithMutableData:passwordMemberArchive];
[ka encodeObject:password];
[ka finishEncoding];
[ka release];
// In the action method when you get the UISwitch action message ---
// when your switch changes state
if(isOn) {
NSString *text = [NSString stringWithString:password.text];
NSKeyedUnarchiver *kua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
UITextField *tf = [kua decodeObject];
[kua finishDecoding];
[kua release];
tf.inputAccessoryView = textField.inputAccessoryView;
tf.frame = textField.frame;
BOOL isFirstResponder = [textField isFirstResponder];
[scrollView insertSubview:tf aboveSubview:textField];
if(isFirstResponder) {
[tf becomeFirstResponder];
}
[textField removeFromSuperview];
self.password = tf;
if([text length]) {
if(isFirstResponder) {
// http://stackoverflow.com/questions/1317929/insert-string-at-cursor-position-of-uitextfield
// Get a reference to the system pasteboard
UIPasteboard* lPasteBoard = [UIPasteboard generalPasteboard];
// Save the current pasteboard contents so we can restore them later
NSArray* lPasteBoardItems = [lPasteBoard.items copy];
// Update the system pasteboard with my string
lPasteBoard.string = text;
// Paste the pasteboard contents at current cursor location
[tf paste:self];
// Restore original pasteboard contents
lPasteBoard.items = lPasteBoardItems;
[lPasteBoardItems release];
} else {
tf.text = text;
}
}
} else {
textField.secureTextEntry = NO;
}
With iOS, you should never try to "hack" stuff, if the behavior you want is not provided by the framework, change your mind !
First its easier ^^, second the user will not be responsive to this, then you never know if the next iOS update will break it or not, so it can be dangerous for your application.
"You want the user sees the password he is taping on a secured textfield", you can display a UILabel in the bottom instead ? Or a confirmation Alert box with the clear password ?
Swift version of Sandy's solution.
if #available(iOS 9.2, *) {
passwordTextField.secureTextEntry = !passwordTextField.secureTextEntry
}
else {
let wasFirstResponder = passwordTextField.isFirstResponder()
if wasFirstResponder {
passwordTextField.resignFirstResponder()
}
passwordTextField.secureTextEntry = !passwordTextField.secureTextEntry
if wasFirstResponder {
passwordTextField.becomeFirstResponder()
}
}

UIScrollView loading an image

i wanted to find an "easy" way to use the pinch/zoom function on my app!
so i decided to use a UIScrollView.
so far so good.
i load my image from an sqlite db like so:
- (void)viewWillAppear:(BOOL)animated {
imageView.image = entity.Aattribute;
}
- (void)viewDidLoad {
self.title = #"Title";
imageView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.backgroundColor = [UIColor blackColor];
myScrollView.contentSize = CGSizeMake(imageView.frame.size.width, imageView.frame.size.height);
myScrollView.maximumZoomScale = 4.0;
myScrollView.minimumZoomScale = 0.75;
myScrollView.clipsToBounds = YES;
myScrollView.delegate = self;
[myScrollView addSubview:imageView];
self.view = myScrollView;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return imageView;
}
any help would be appreciated!
thank you for your time!
EDIT:
i m just gonna answer my own question here! (i m getting better at this!!! LOL)
up above is the working code. i have edited it so if anyone needs this can refer to it!
thanks for the answers!
This looks mostly correct. A couple of notes:
you've misspelled "viewDidLoad". This could be a side effect of typing it into the browser window. If it's wrong in your code, then it's certainly not helping things be better.
viewForZoomingInScrollView: is expecting you to return a UIView. myImage sounds suspiciously unlike a UIView. You should be returning the imageView instance variable. That's what's getting moved around.
Edit
From the documentation:
The UIScrollView class can have a
delegate that must adopt the
UIScrollViewDelegate protocol. For
zooming and panning to work, the
delegate must implement both
viewForZoomingInScrollView: and
scrollViewDidEndZooming:withView:atScale:;
in addition, the maximum
(maximumZoomScale) and minimum (
minimumZoomScale) zoom scale must be
different.
So it looks like you still need to implement another delegate method.

Resources