Drawing Image with CGImage? - cocoa

I have a CGImageRef and I want to display it on an NSView. I already have an CGImageRef from source path but the following doesn't work:
- (void)drawRect:(NSRect)rect {
NSString * thePath = [[NSBundle mainBundle] pathForResource: #"blue_pict"
ofType: #"jpg"];
NSLog(#"the path : %#", thePath);
CGImageRef myDrawnImage = [self createCGImageRefFromFile:thePath];
NSLog(#"get the context");
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
if (context==nil) {
NSLog(#"context failed");
return;
}
//get the bitmap context
CGContextRef myContextRef = CreateARGBBitmapContext(myDrawnImage);
//set the rectangle
NSLog(#"get the size for imageRect");
size_t w = CGImageGetWidth(myDrawnImage);
size_t h = CGImageGetHeight(myDrawnImage);
CGRect imageRect = {{0,0}, {w,h}};
NSLog(#"W : %d", w);
myDrawnImage = CGBitmapContextCreateImage(myContextRef);
NSLog(#"now draw it");
CGContextDrawImage(context, imageRect, myDrawnImage);
char *bitmapData = CGBitmapContextGetData(myContextRef);
NSLog(#"and release it");
CGContextRelease(myContextRef);
if (bitmapData) free(bitmapData);
CGImageRelease(myDrawnImage);
}
What's wrong with it?

CGImageRef myDrawnImage = [self createCGImageRefFromFile:thePath];
Now you have your image.
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
Now you have your view's context. You have everything you need to draw the image.
CGContextRef myContextRef = CreateARGBBitmapContext(myDrawnImage);
Wait, what?
myDrawnImage = CGBitmapContextCreateImage(myContextRef);
O…kay… now you have captured the contents of a context that nothing has drawn in, forgetting about (and leaking) your loaded image by replacing it with a blank image.
CGContextDrawImage(context, imageRect, myDrawnImage);
You draw the blank image.
Cut out the creation of a bitmap context and the creation of an image of the contents of that context, and just draw the image you loaded into the context for your view.
Or use NSImage. That would be a two-liner.

Yes, you don't actually draw the image. All you need to do is use CGContextDrawImage instead of creating an empty bitmap context.

Related

How can I show an image in a NSView using an CGImageRef image

I want to show an image in NSview or in the NSImageView. In my header file I have
#interface FVView : NSView
{
NSImageView *imageView;
}
#end
here is what I been trying to do in my implementation file:
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
(Here I get an image called fitsImage........ then I do)
//Here I make the image
CGImageRef cgImage = CGImageRetain([fitsImage CGImageScaledToSize:maxSize]);
NSImage *imageR = [self imageFromCGImageRef:cgImage];
[imageR lockFocus];
//Here I have the view context
CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
//Here I set the via dimensions
CGRect renderRect = CGRectMake(0., 0., maxSize.width, maxSize.height);
[self.layer renderInContext:ctx];
[imageR unlockFocus];
CGContextDrawImage(ctx, renderRect, cgImage);
CGImageRelease(cgImage);
}
I don't get anything in the NSview window when I run the script. No errors at all I just can't see what I'm doing wrong. My Xcode version in 5.1.1
I'm trying to learn how to manipulate CGImageRef and view it in a window or nsview.
Thank you.
I'm not quite sure what exactly your setup is. Drawing an image in a custom view is a separate thing from using an NSImageView. Also, a custom view that may (or may not) be layer-backed is different from a layer-hosting view.
You have a lot of the right elements, but they're all mixed up together. In no case do you have to lock focus on an NSImage. That's for drawing into an NSImage. Also, a custom view that subclasses from NSView doesn't have to call super in its -drawRect:. NSView doesn't draw anything.
To draw an image in a custom view, try:
- (void) drawRect:(NSRect)dirtyRect
{
CGImageRef cgImage = /* ... */;
NSSize maxSize = /* ... */;
CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
CGRect renderRect = CGRectMake(0., 0., maxSize.width, maxSize.height);
CGContextDrawImage(ctx, renderRect, cgImage);
CGImageRelease(cgImage);
}
If you have an NSImageView, then you don't need a custom view or any drawing method or code. Just do the following at the point where you obtain the image or the information necessary to generate it:
NSImageView* imageView = /* ... */; // Often an outlet to a view in a NIB rather than a local variable.
CGImageRef cgImage = /* ... */;
NSImage* image = [[NSImage alloc] initWithCGImage:cgImage size:/* ... */];
imageView.image = image;
CGImageRelease(cgImage);
If you're working with a layer-hosting view, you just need to set the CGImage as the layer's content. Again, you do this whenever you obtain the image or the information necessary to generate it. It's not in -drawRect:.
CALayer* layer = /* ... */; // Perhaps someView.layer
CGImageRef cgImage = /* ... */;
layer.contents = (__bridge id)cgImage;
CGImageRelease(cgImage);

can we save the contents drawn on the pdf as a new pdf ? xcode

Can we save the contents drawn over the pdf , as pdf again , i tried to save the pdf with the contents but with no luck , it saves with blank pdf
the code:
- (void)drawRect:(CGRect)rect
{
NSString *pathToPdfDoc = [[NSBundle mainBundle] pathForResource:#"myPDF" ofType:#"pdf"];
NSURL *pdfUrl = [NSURL fileURLWithPath:pathToPdfDoc];
document = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdfUrl);
size_t count = CGPDFDocumentGetNumberOfPages (document);// 3
if (count == 0)
{
NSLog(#"PDF needs at least one page");
return;
}
CGRect paperSize = CGRectMake(0.0,0.0,595.28,841.89);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
// flip context so page is right way up
CGContextTranslateCTM(currentContext, 0, paperSize.size.height);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGPDFPageRef page = CGPDFDocumentGetPage (document, 1); // grab page 1 of the PDF
CGContextDrawPDFPage (currentContext, page); // draw page 1 into graphics context
// flip context so annotations are right way up
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextTranslateCTM(currentContext, 0, -paperSize.size.height);
////////////////////////////////
[curImage drawAtPoint:CGPointMake(0, 0)];
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextStrokePath(context);
[super drawRect:rect];
// get a temprorary filename for this PDF
NSString* path = [[NSBundle mainBundle] pathForResource:#"sampleData" ofType:#"plist"];
path = NSTemporaryDirectory();
self.pdfFilePath = [path stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.pdf", [[NSDate date] timeIntervalSince1970] ]];
UIGraphicsBeginPDFContextToFile(#"test.pdf", paperSize, nil);
[#"annotation" drawInRect:CGRectMake(100.0, 100.0, 200.0, 40.0) withFont:[UIFont systemFontOfSize:18.0]];
}
And I tried to close the PDF on button click
- (IBAction)buttonPressed:(UIButton *)button {
UIGraphicsEndPDFContext();
return;
}
But it's not working.
Any help will be greatly appreciated , thanks in advance
figured it out , here is the function to create the pdf with the contents drawn or annotated on it
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName: (NSString*)aFilename
{
// Creates a mutable data object for updating with binary data, like a byte array
NSMutableData *pdfData = [NSMutableData data];
// Points the pdf converter to the mutable data object and to the UIView to be converted
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView.layer renderInContext:pdfContext];
// remove PDF rendering context
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectory);
}
and called the function on button click .

Mac App Store like toolbar with noise

I found this great subclass of NSWindow, however it does not add any noise to the gradient of the toolbar. If you look closely at The App Store, Reeder or Twitter they all have noise over the gradient.
How do I add noise to a gradient?
I found this thread but I don't understand how to put this into code.
First of all the nessesary code has been added to INAppStoreWindow, so i have no use case for this anymore. However for people who would like to know how to do this, heres how it's done by INAppStoreWindow.
First a function for making the image with noise is created.
static CGImageRef createNoiseImageRef(NSUInteger width, NSUInteger height, CGFloat factor)
{
NSUInteger size = width*height;
char *rgba = (char *)malloc(size); srand(124);
for(NSUInteger i=0; i < size; ++i){rgba[i] = rand()%256*factor;}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef bitmapContext =
CGBitmapContextCreate(rgba, width, height, 8, width, colorSpace, kCGImageAlphaNone);
CFRelease(colorSpace);
free(rgba);
CGImageRef image = CGBitmapContextCreateImage(bitmapContext);
CFRelease(bitmapContext);
return image;
}
Then the image is used to overlay the noise over the current graphics
static CGImageRef noisePattern = nil;
if (noisePattern == nil) noisePattern = createNoiseImageRef(128, 128, 0.015);
[NSGraphicsContext saveGraphicsState];
[[NSGraphicsContext currentContext] setCompositingOperation:NSCompositePlusLighter];
CGRect noisePatternRect = CGRectZero;
noisePatternRect.size = CGSizeMake(CGImageGetWidth(noisePattern), CGImageGetHeight(noisePattern));
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGContextDrawTiledImage(context, noisePatternRect, noisePattern);
[NSGraphicsContext restoreGraphicsState];

Using CALayer's renderInContext: method with geometryFlipped

I have a CALayer (containerLayer) that I'm looking to convert to a NSBitmapImageRep before saving the data out as a flat file. containerLayer has its geometryFlipped property set to YES, and this seems to be causing issues. The PNG file that is ultimately generated renders the content correctly, but doesn't seem to takes the flipped geometry into account. I'm obviously looking for test.png to accurately represent the content shown to the left.
Attached below is a screenshot of the problem and the code I'm working with.
- (NSBitmapImageRep *)exportToImageRep
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
int bitmapByteCount;
int bitmapBytesPerRow;
int pixelsHigh = (int)[[self containerLayer] bounds].size.height;
int pixelsWide = (int)[[self containerLayer] bounds].size.width;
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
context = CGBitmapContextCreate (NULL,
pixelsWide,
pixelsHigh,
8,
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
if (context == NULL)
{
NSLog(#"Failed to create context.");
return nil;
}
CGColorSpaceRelease(colorSpace);
[[[self containerLayer] presentationLayer] renderInContext:context];
CGImageRef img = CGBitmapContextCreateImage(context);
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:img];
CFRelease(img);
return bitmap;
}
For reference, here's the code that actually saves out the generated NSBitmapImageRep:
NSData *imageData = [imageRep representationUsingType:NSPNGFileType properties:nil];
[imageData writeToFile:#"test.png" atomically:NO];
You need to flip the destination context BEFORE you render into it.
Update your code with this, I have just solved the same problem:
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, pixelsHigh);
CGContextConcatCTM(context, flipVertical);
[[[self containerLayer] presentationLayer] renderInContext:context];

Copying the drawn contents of one UIView to another

I'd like to take a UITextView and allow the user to enter text into it and then trigger a copy of the contents onto a quartz bitmap context. Does anyone know how I can perform this copy action? Should I override the drawRect method and call [super drawRect] and then take the resulting context and copy it? If so, does anyone have any reference to sample code to copy from one context to another?
Update: from reading the link in the answer below, I put together this much to attempt to copy my UIView contents into a bitmap context, but something is still not right. I get my contents mirrored across the X axis (i.e. upside down). I tried using CGContextScaleCTM() but that seems to have no effect.
I've verified that the created UIImage from the first four lines do properly create a UIImage that isn't strangely rotated/flipped, so there is something I'm doing wrong with the later calls.
// copy contents to bitmap context
UIGraphicsBeginImageContext(mTextView.bounds.size);
[mTextView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setNeedsDisplay];
// render the created image to the bitmap context
CGImageRef cgImage = [image CGImage];
CGContextScaleCTM(mContext, 1.0, -1.0); // doesn't seem to change result
CGContextDrawImage(mContext, CGRectMake(
mTextView.frame.origin.x,
mTextView.frame.origin.y,
[image size].width, [image size].height), cgImage);
Any suggestions?
Here is the code I used to get a UIImage of UIView:
#implementation UIView (Sreenshot)
- (UIImage *)screenshot{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);
/* iOS 7 */
BOOL visible = !self.hidden && self.superview;
CGFloat alpha = self.alpha;
BOOL animating = self.layer.animationKeys != nil;
BOOL success = YES;
if ([self respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)]){
//only works when visible
if (!animating && alpha == 1 && visible) {
success = [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
}else{
self.alpha = 1;
success = [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
self.alpha = alpha;
}
}
if(!success){ /* iOS 6 */
self.alpha = 1;
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
self.alpha = alpha;
}
UIImage* img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
#end
You can use in iOS 7 and later:
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

Resources