I'm using this code to colorize some images of a UIButton subclass:
UIImage *img = [self imageForState:controlState];
// begin a new image context, to draw our colored image onto
UIGraphicsBeginImageContextWithOptions(img.size, NO, 0.0f);
// get a reference to that context we created
CGContextRef context = UIGraphicsGetCurrentContext();
// set the fill color
[self.buttonColor setFill];
CGContextSetAllowsAntialiasing(context, true);
CGContextSetShouldAntialias(context, true);
// translate/flip the graphics context (for transforming from CG* coords to UI* coords
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// set the blend mode to multiply, and the original image
CGContextSetBlendMode(context, kCGBlendModeScreen);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, rect, img.CGImage);
// set a mask that matches the shape of the image, then draw the colored image
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//return the colored image
[self setImage:coloredImg forState:controlState];
But the images come out with rough edges. I've tried using screen, lighten, and plusLighter blend modes, because some of the images have white parts that I want to stay white. The only part I want colorized is the black areas. I've attached the original button images, and after they've been colorized. I can't get the edges to look good. When I had them as white images that were colorized using multiply blend mode, it looked much better. But I want to use black so I can use one method for colorizing images with and without white in them. I tried with anti-aliasing, that didn't help either. It looks like it just isn't anti-aliasing it. I haven't worked with Core Graphics enough to know what's up with it.
EDIT
Here's what the original PNGs look like:
and here's what it should look like:
and here's what it does look like:
The size if different, but you can see the bad quality around the edges.
Maybe your original icons (PNGs?) are just "too sharp"? Could you show us? You just draw the image at its original size without resizing, so the problem could be right from the start.
I'm not sure what is what you are trying to accomplish here. Are you trying to round the edges of the images? If so, you are better of by changing the round corner property of the UIButton's layer. Since UIButton is a subclass of UIView, you can get its layer property and change the edge color and round its corner.
Related
I would like to apply a saturation blendMode using a UIView on other views, like this :
: Example
I have a UILabel and UIImageView in the view of my ViewController, plus a BlendView which overwrites drawRect method.
- (void) drawRect:(CGRect)area
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
[[UIColor whiteColor] setFill];
CGContextSetBlendMode(context, kCGBlendModeSaturation);
CGContextFillRect(context, self.bounds);
CGContextRestoreGState(context);
}
However, I only have a black square if kCGBlendModeSaturation is given and a white square if kCGBlendModeMultiply give - the blendmode isn't applied.
I have set the BlendView as not opaque and tested with different alpha value, no changes.
P.S.: I wish the blendmode to be applied on any views, not only images.
You can't apply a blend mode to a single view and hope it'll have any effect on the views below, like you would with layers in Photoshop (the only compositing you can do between two separate views are change their opacity).
The only thing you can do is, on a single view's drawRect, draw with a specific fill mode over the previous drawing instructions.
I have an MKMapView where I want to grey out parts of the map. More specifically, I want to have some circles and rectangles which are displayed normally and the rest of the map has a semi-transparent grey layer. Something like this:
For that, I think that I should subclass MKOverlay and MKOverlayRenderer. As Apple suggests, in my MKOverlayRenderer subclass, I should override the drawMapRect:zoomScale:inContext: method, and draw my stuff using Core Graphics. My question is how can I draw the following with Core Graphics?
I have spent some hours looking at masking and clipping using Core Graphics, but I haven't found anything similar to this. The QuartzDemo has some examples of clipping and masking. I guess clipping with either the even-odd or nonzero winding number rules won't work for me, as the rectangles and circles are dynamic. I think I have to create a mask somehow, but I can't figure out how. The QuartzDemo creates a mask out of an image. How could I create a mask using rectangles and circles? Is there any other way I could approach this?
Thank you
You should be able to setup a context with transparency, draw the skinny rectangles and circles without stroking them, draw a rectangle border around the whole context, and then fill the shape with the darker color. You'll need to look into fill order rules to make sure that the larger space is what is filled rather than the smaller joined shapes.
I guess clipping with either the even-odd or nonzero winding number rules won't work for me, as the rectangles and circles are dynamic.
This shouldn't matter.
Well, I worked it out myself. I have to create my own mask. This is done by drawing the "hole" shapes, outputting this to an ImageRef, and inverting this ImageRef to output the mask. Here is a code snippet that could work if you add it in the QuartzDemo project->QuartzClipping.m->QuartzClippingView class, at the end of the drawInContext method.
-(void)drawInContext:(CGContextRef)context {
// ...
CGContextSaveGState(context);
// dimension of the square mask
int dimension = 20;
// create mask
UIGraphicsBeginImageContextWithOptions(CGSizeMake(dimension, dimension), NO, 0.0f);
CGContextRef newContext = UIGraphicsGetCurrentContext();
// draw overlapping circle holes
CGContextFillEllipseInRect(newContext, CGRectMake(0, 0, 10, 10));
CGContextFillEllipseInRect(newContext, CGRectMake(0, 7, 10, 10));
// draw mask
CGImageRef mask = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());
UIGraphicsEndImageContext();
// the inverted mask is what we need
CGImageRef invertedMask = [self invertMask:mask dimension:dimension];
CGRect rectToDraw = CGRectMake(210.0, height - 290.0, 90.0, 90.0);
// everything drawn in rectToDraw after this will have two holes
CGContextClipToMask(context, rectToDraw, invertedMask);
// drawing a red rectangle for this demo
CGContextFillRect(context, rectToDraw);
CGContextRestoreGState(context);
}
// taken from the QuartzMaskingView below
- (CGImageRef)invertMask:(CGImageRef)originalMask dimension:(int)dimension{
// To show the difference with an image mask, we take the above image and process it to extract
// the alpha channel as a mask.
// Allocate data
NSMutableData *data = [NSMutableData dataWithLength:dimension * dimension * 1];
// Create a bitmap context
CGContextRef context = CGBitmapContextCreate([data mutableBytes], dimension, dimension, 8, dimension, NULL, (CGBitmapInfo)kCGImageAlphaOnly);
// Set the blend mode to copy to avoid any alteration of the source data
CGContextSetBlendMode(context, kCGBlendModeCopy);
// Draw the image to extract the alpha channel
CGContextDrawImage(context, CGRectMake(0.0, 0.0, dimension, dimension), originalMask);
// Now the alpha channel has been copied into our NSData object above, so discard the context and lets make an image mask.
CGContextRelease(context);
// Create a data provider for our data object (NSMutableData is tollfree bridged to CFMutableDataRef, which is compatible with CFDataRef)
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFMutableDataRef)data);
// Create our new mask image with the same size as the original image
return CGImageMaskCreate(dimension, dimension, 8, 8, dimension, dataProvider, NULL, YES);
}
Any easier/more efficient solution is welcome :)
When user hovers over the image,tapping on certain body part presents the magnified image of that area. I wanted to know any possible third party frameworks that address such kind of feature or code snippets(like using which gesturerecognizers) that can help me attain this feature.
Question 2: Also I have to add a dynamic clickable Label at the point where the touch happens and ends (as you can see the wrist label in image) so that I can take the user to a separate view from this screen on clicking the label. How to make this possible?
In your drawRect method, mask off a circle (using a monochrome bitmap containing the 'mask' of your magnifying glass) and draw your subject view in there with a 2x scale transform. Then draw a magnifying glass image over that and you're done.
- (void) drawRect: (CGRect) rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect bounds = self.bounds;
CGImageRef mask = [UIImage imageNamed: #"loupeMask"].CGImage;
UIImage *glass = [UIImage imageNamed: #"loupeImage"];
CGContextSaveGState(context);
CGContextClipToMask(context, bounds, mask);
CGContextFillRect(context, bounds);
CGContextScaleCTM(context, 2.0, 2.0);
//draw your subject view here
CGContextRestoreGState(context);
[glass drawInRect: bounds];
}
Check this link for complete example.
I'm trying to zoom and translate an image on the screen.
here's my drawRect:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetShouldAntialias(context, NO);
CGContextScaleCTM (context, senderScale, senderScale);
[self.image drawAtPoint:CGPointMake(imgposx, imgposy)];
CGContextRestoreGState(context);
}
When senderScale is 1.0, moving the image (imgposx/imgposy) is very smooth. But if senderScale has any other value, performance takes a big hit and the image stutters when I move it.
The image I am drawing is a UIImageobject. I create it with
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
and draw a simple UIBezierPath(stroke):
self.image = UIGraphicsGetImageFromCurrentImageContext();
Am I doing something wrong? Turning off the anti-aliasing did not improve things much.
Edit:
I tried this:
rectImage = CGRectMake(0, 0, self.frame.size.width * senderScale, self.frame.size.height * senderScale);
[image drawInRect:rectImage];
but it was just as slow as the other method.
If you want this to perform well, you should let the GPU do the heavy lifting by using CoreAnimation instead of drawing the image in your -drawRect: method. Try creating a view and doing:
myView.layer.contents = self.image.CGImage;
Then zoom and translate it by manipulating the UIView relative to its superview. If you draw the image in -drawRect: you're making it do the hard work of blitting the image for every frame. Doing it via CoreAnimation only blits once, and then subsequently lets the GPU zoom and translate the layer.
Im trying to rotate an image, that i want to draw on a image file (to the context.
Everything works fine, except when i rotate the image.
Basically i have an image; i want to scale and resize the image and then clip it to a rect and finaly draw it to the UICurrentContext;
//create a new graphic context
UIGraphicsBeginImageContext(CGSizeMake(500, 500));
CGContextRef graphicContext = UIGraphicsGetCurrentContext();
CGContextDrawImage(graphicContext, CGRectMake(0, 0, rect.size.width, rect.size.height), image.CGImage);
CGContextRotateCTM(graphicContext, 45 * M_PI/180.0);
UIImage* editedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//draw it to the current context
[editedImage drawAtPoint:CGPointMake(0,0)];
The thing is when i rotate the image, i dont have a clue what the new size of the imagecontext would be. Next to that, the image in the edited image is not rotated..
You need to rotate the context before you draw. I know this seems silly, but to use the analogy of a piece of paper...this is a piece of paper that cannot move. Instead you must move yourself around it and then draw.
This way, your size will never change because anything too big will just simply be cut off.
EDIT Also, this function takes radians, not degrees, so you don't need to convert as you are doing. If you want 45 degrees it will just be PI / 4 (which is stored as the constant M_PI_4 in math.h).