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];
Related
When you use .imageView.adjustsImageWhenAncestorFocused you get your image scaled, you get it bigger on focus. The image goes beyond it's bounds. That means that you can't see full image - it's coped on all sides.
You have:
You want:
If you want it work from the box, you need to remember to reserve Focused/Safe zone size (from documentation) when you create your image.
In my case I have my images from server and I can't edit them.
What worked for me - it's to redraw image right before setting:
UIImage *oldImage = [UIImage imageNamed:#"example"];
CGFloat width = cell.imageView.frame.size.width;
CGFloat height = cell.imageView.frame.size.height;
CGSize newSize = CGSizeMake(width, height);
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGImageRef imageRef = oldImage.CGImage;
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
CGContextRef resizeContext = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(resizeContext, kCGInterpolationHigh);
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);
CGContextConcatCTM(resizeContext, flipVertical);
CGContextDrawImage(resizeContext, newRect, imageRef);
CGImageRef newImageRef = CGBitmapContextCreateImage(resizeContext);
UIImage *newImageResized = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = newImageResized;
});
It's nor necessary to redraw in this way, but the point is you need to draw your UIImage again in order to have your image displayed properly.
Code from here.
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];
I am trying to draw an image using core graphics such that it has rounded corners and a drop shadow. Here is a snippet of my code:
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 2, shadowColor);
CGContextAddPath(context, path);
CGContextClip(context);
CGContextDrawImage(context, rect, image);
The problem I am having is that the clipping to create the rounded corners is also clipping the shadow. Since the image may be transparent in areas, I cannot simply draw the rounded rectangle with a shadow under the image. I guess I need to apply the rounded shape to the image first, and then draw the resulting image to the screen and add the shadow. Does anyone know how to do this?
Thanks!
Okay, so assuming that you have a UIView subclass, which has an instance variable, image, which is a UIImage, then you can do your drawRect: function like so...
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGRect _bounds = [self bounds];
CGColorRef aColor;
CGContextRef context = UIGraphicsGetCurrentContext();
// Create a path
CGRect insetRect = CGRectInset(_bounds, kBSImageButtonBorder, kBSImageButtonBorder);
CGRect offsetRect = insetRect; offsetRect.origin = CGPointZero;
UIGraphicsBeginImageContext(insetRect.size);
CGContextRef imgContext = UIGraphicsGetCurrentContext();
CGPathRef clippingPath = [UIBezierPath bezierPathWithRoundedRect:offsetRect cornerRadius:CORNER_RADIUS].CGPath;
CGContextAddPath(imgContext, clippingPath);
CGContextClip(imgContext);
// Draw the image
[image drawInRect:offsetRect];
// Get the image
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Setup the shadow
aColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f].CGColor;
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 2.0f), 2.0f, aColor);
// Draw the clipped image in the context
[img drawInRect:insetRect];
}
I'm a little new to Quartz programming myself, but that should give you your image, centered in the rectangle, minus a border, with a corner radius, and a 2.f point shadow 2.f points below it. Hope that helps.
Here is a function to round the corners of an image using Daniel Thorpe's answer, in case you came here, like me, just looking for a way to do this.
+ (UIImage *) roundCornersOfImage:(UIImage *)image toRadius:(float)radius {
// create image sized context
UIGraphicsBeginImageContext(image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// add rounded rect clipping path to context using radius
CGRect imageBounds = CGRectMake(0, 0, image.size.width, image.size.height);
CGPathRef clippingPath = [UIBezierPath bezierPathWithRoundedRect:imageBounds cornerRadius:radius].CGPath;
CGContextAddPath(context, clippingPath);
CGContextClip(context);
// draw the image
[image drawInRect:imageBounds];
// get the image
UIImage *outImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outImage;
}
You can use an imageView with a layer, the layer has properties for setting shadows and borders, this is how:
self.imageView = [[NSImageView alloc] initWithFrame:NSMakeRect(0,0,60,60)];
self.imageView.image = [NSImage imageNamed:#"yourImageName"];
self.imageView.wantsLayer = YES;
self.imageView.layer.cornerRadius = 10;
//make the shadow and set it
NSShadow* shadow = [[NSShadow alloc] init];
shadow.shadowBlurRadius = 2;
shadow.shadowOffset = NSMakeSize(2, -2);
shadow.shadowColor = [NSColor blackColor];
self.imageView.shadow = shadow;
Hope this helps, this is also much faster to draw then using drawRect overrides
I have a project that needs to draw text in a view with a gradient fill in a custom subclass of NSView, like this example below.
I'm wondering how I can achieve this, as I'm pretty new to Cocoa drawing.
Try creating an alpha mask from the text then using NSGradient to draw into it. Here's a simple example based on the linked code:
- (void)drawRect:(NSRect)rect
{
// Create a grayscale context for the mask
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
CGContextRef maskContext =
CGBitmapContextCreate(
NULL,
self.bounds.size.width,
self.bounds.size.height,
8,
self.bounds.size.width,
colorspace,
0);
CGColorSpaceRelease(colorspace);
// Switch to the context for drawing
NSGraphicsContext *maskGraphicsContext =
[NSGraphicsContext
graphicsContextWithGraphicsPort:maskContext
flipped:NO];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:maskGraphicsContext];
// Draw the text right-way-up (non-flipped context)
[text
drawInRect:rect
withAttributes:
[NSDictionary dictionaryWithObjectsAndKeys:
[NSFont fontWithName:#"HelveticaNeue-Bold" size:124], NSFontAttributeName,
[NSColor whiteColor], NSForegroundColorAttributeName,
nil]];
// Switch back to the window's context
[NSGraphicsContext restoreGraphicsState];
// Create an image mask from what we've drawn so far
CGImageRef alphaMask = CGBitmapContextCreateImage(maskContext);
// Draw a white background in the window
CGContextRef windowContext = [[NSGraphicsContext currentContext] graphicsPort];
[[NSColor whiteColor] setFill];
CGContextFillRect(windowContext, rect);
// Draw the gradient, clipped by the mask
CGContextSaveGState(windowContext);
CGContextClipToMask(windowContext, NSRectToCGRect(self.bounds), alphaMask);
NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor blackColor] endingColor:[NSColor grayColor]];
[gradient drawInRect:rect angle:-90];
[gradient release];
CGContextRestoreGState(windowContext);
CGImageRelease(alphaMask);
}
This uses the view bounds as the gradient bounds; if you wanted to be more accurate, you'd need to get the text height (information about that here).
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.