Core Graphics Blending Causes Bright Spots - objective-c

I'm trying to draw a simple grid pattern on a black background using Core Graphics. The gridlines will be green. I am currently creating and drawing in an image context, then using the resulting image to pattern the background of my view:
CGFloat gridSize = 45;
UIGraphicsBeginImageContext(CGSizeMake(gridSize, gridSize));
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(ctx);
// Create black background fill.
CGContextSetFillColorWithColor(ctx, [[UIColor colorWithRed:0.0
green:0.0
blue:0.0
alpha:1.0] CGColor]);
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, gridSize, 0);
CGContextAddLineToPoint(ctx, gridSize, gridSize);
CGContextAddLineToPoint(ctx, 0, gridSize);
CGContextAddLineToPoint(ctx, 0, 0);
CGContextFillPath(ctx);
// Set context variables for line drawing.
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
CGContextSetStrokeColorWithColor(ctx, [[UIColor colorWithRed:0.0
green:1.0
blue:0.0
alpha:1.0] CGColor]);
CGContextSetLineWidth(ctx, 1);
// Draw top and bottom grid lines.
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, gridSize, 0);
CGContextMoveToPoint(ctx, gridSize, gridSize);
CGContextAddLineToPoint(ctx, 0, gridSize);
CGContextStrokePath(ctx);
// Draw left and right grid lines.
CGContextMoveToPoint(ctx, gridSize, 0);
CGContextAddLineToPoint(ctx, gridSize, gridSize);
CGContextMoveToPoint(ctx, 0, gridSize);
CGContextAddLineToPoint(ctx, 0, 0);
CGContextStrokePath(ctx);
// Draw a "cross" in the center of the image to troubleshoot blending.
CGContextMoveToPoint(ctx, 20, 20);
CGContextAddLineToPoint(ctx, 40, 20);
CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, 30, 10);
CGContextAddLineToPoint(ctx, 30, 30);
CGContextStrokePath(ctx);
UIGraphicsPopContext();
UIImage *gridImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Set background color to a pattern based on the drawn image.
[self setBackgroundColor:[[UIColor alloc] initWithPatternImage:gridImage]];
However, this code results in the following:
Note that there are brighter areas that occur at the intersection of any lines. The crosses that are somewhat randomly placed within the grid pattern were put there just to test whether the bright areas still occur when not affected by the edges of the patterning (they do).
It is these bright areas at the line intersections that I wish to alleviate. To make sure I hadn't accidentally fooled myself using some sort of optical illusion, I checked the pixel colors in the middle of the lines as well as at their intersections.
Pixel values at midpoint of green line:
R: 0
G: 129
B: 0
Pixel values at intersection of two green lines:
R: 0
G: 191
B: 0
I have checked the Apple Core Graphics docs regarding blending modes, and ascertained that the default blending mode is kCGBlendModeNormal, which should, when painting colors with alpha = 1, should "completely obscure the background", and any previously painted colors. Though this is the default blending mode, I still set it explicitly in the above code, and yet I still see these bright squares.
What I expect instead is to see the lines of a uniform green color, presumably with the RGB profile:
R: 0
G: 255
B: 0
What am I missing?

It looks as if the lines are drawn with an alpha of 50% but double the specified line width. This happens if you have a line width of 1 pixel but draw horizontal or vertical lines using integer coordinates.
Your coordinates specify an infinitely thin line from the start to the end coordinates. The drawn line should then extend to both sides of the thin line by half the stroke width. In your case, this means the line would need to cover half a pixel to the left and to the right or to the top and to the bottom of grid. Since only full pixels can be drawn, the alpha is reduced to 50% and the full pixel drawn instead.
The solution is simple: Add 0.5 to all your coordinates.

Related

Lines drawn with core graphics that are set to the same width sometimes vary in size when drawn

Here is a picture of two lines drawn in a UITableViewCell with the same function, and same width, and color
As you can see the bottom line is a lot thicker than the other line.
The code I am using for drawing:
[CSDrawing drawLineWithColor:[UIColor blackColor] width:1.0 yPosition:1.0 rect:rect];
[CSDrawing drawLineWithColor:[UIColor blackColor] width:1.0 yPosition:CGRectGetMaxY(rect) - 3.0 rect:rect]; // draw a line on top and bottom
+(void)drawLineWithColor:(UIColor *)color width:(CGFloat)width yPosition:(CGFloat)yPosition rect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextMoveToPoint(context, 0.0, yPosition);
CGContextAddLineToPoint(context, CGRectGetMaxX(rect), yPosition);
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGContextSetLineWidth(context, width);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
The problem was with the backgroundView being stretched to fit the content of the cell when the cell was being reused. When the cell was bigger the pixels were stretched. This is solved be setting the contentMode property to UIViewContentModeRedraw

need assistance regarding growing & shrinking circle from centre in quartz-2d

I am currently working on drawing app, in which have a slider to increase and decrease line width. I just want to do a simple thing that a circle in front of slider to present a width. I easily did that but its not growing and shrinking from centre, its growing and shrinking from top x, y, here is the code
- (UIImage *)circleOnImage:(int)size
{
UIGraphicsBeginImageContext(CGSizeMake(25, 25));
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] setFill];
CGContextTranslateCTM(ctx, 12, 12);//also try to change the coordinate but didn't work
CGRect circleRect = CGRectMake(0, 0, size, size);
CGContextFillEllipseInRect(ctx, circleRect);
UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return retImage;
}
Try
CGContextTranslateCTM(ctx, 12.5, 12.5);
CGRect circleRect = CGRectMake(-size/2., -size/2., size, size);

drawing dotted lines from UIViewController

I have a UIViewController which has a UIView in it and I want to draw a straight dotted line on it. What is the easiest and performant way to do this? How do I do this? It will just be a straight horizontal dotted line of width 150 with gray color. I've looked around and it seems that using
CGContextSetLineDash is the solution and here's what I have so far:
-(void) drawRect:(CGRect) rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 100);
CGFloat dashes[] = {1,1};
CGContextSetLineDash(context, 2.0, dashes, 2);
CGContextStrokePath(context);
}
CGContextStrokePath draws the current path, however, you never add any path to your context, so nothing is drawn.
Add the following two lines before your CGContextStrokePath call and you should see something:
CGContextMoveToPoint(context, 0, self.bounds.size.height * 0.5);
CGContextAddLineToPoint(context, self.bounds.size.width, self.bounds.size.height * 0.5);
If you haven't already set your stroke color, you should also add something like this (50% gray):
CGContextSetRGBStrokeColor(context, 0.5, 0.5, 0.5, 1.0);
I'm not sure if the result is really what you want, a 100 point dotted line with a pattern of {1, 1} will look more like an rectangle with a pinstripe pattern than like a dotted line. If you want your dots to be circles, you will have to draw those manually in some sort of loop.
Try this:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGContextSetLineWidth(context, 2.0);
CGFloat lengths[] = {0, 8};
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineDash(context, 0.0f, lengths, 2);
CGContextMoveToPoint(context, 10, 10);
CGContextAddLineToPoint(context, 200, 10);
CGContextStrokePath(context);
CGColorSpaceRelease(colorspace);

CGContextAddEllipse - overlapping get's clipped - Quartz

I like to draw a glass with a few Elements
- Top Ellipse
- Bottom Ellipse
- and the Lines Inbetween
Next, it should be filled with a Gradient. The Elements work, but at the point, where the middle of the glass comes in touch with the top or bottom ellipse the area get's clipped.
- (void)drawRect:(CGRect)rect
{
CGPoint c = self.center;
// Drawing code
CGContextRef cx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(cx, 1.0);
[[UIColor whiteColor] setStroke];
// DrawTheShapeOfTheGlass
CGContextBeginPath(cx);
// Top and Bottom Ellipse
CGContextAddEllipseInRect(cx, CGRectMake(0, 0, 100, 20));
CGContextAddEllipseInRect(cx, CGRectMake(10, 90, 80, 20));
// Define the points for the Area inbetween
CGPoint points[] = { {0.0,10.0},{10.0,100.0},{90.0,100.0},{100.0,10.0} };
CGContextAddLines(cx, points, 4);
CGContextClosePath(cx);
// Clip, that's only the Clipped-Area wil be filled with the Gradient
CGContextClip(cx);
// CreateAndDraw the Gradient
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat colorSpace[] = {1.0f, 1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f };
CGFloat locations[] = { 0.0, 1.0 };
CGGradientRef myGradient = CGGradientCreateWithColorComponents(rgbColorSpace, colorSpace, locations, 2);
CGPoint s = CGPointMake(0, 0);
CGPoint e = CGPointMake(100, 100);
CGContextDrawLinearGradient(cx, myGradient, s, e, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CGColorSpaceRelease(rgbColorSpace);
CGGradientRelease(myGradient);
}
Here how it looks like:
Is there any possibility to "fill" the whole ellipse? I played around with BlendModes but it didn't help.
Thanks
Try replacing the points[] initialization code with the following...
CGPoint points[] = {{0.0,10.0},{100.0,10.0},{90.0,100.0},{10.0,100.0}};
CoreGraphics uses the non-zero winding count rule to determine how to fill a path. Since ellipses are drawn clockwise and your trapezoid was drawn counter clockwise, the overlapping regions were not filled. Changing the drawing order of the trapezoid to clockwise will result in an object that is completely filled.

Drawings in drawRect not being displayed correctly

I want to implement freeform drawing in my app. First, I tried the code inside drawLayer:inContext: and it gave me the result I wanted.
Drawing in CALayer:
But when I decided to implement the code inside drawRect:, this happened:
Even if I draw inside the white space, the drawing is rendered outside as shown above. The code I used is exactly the same. I copy-pasted it from drawLayer:inContext: to drawRect:. I didn't change a thing, so why is this happening?
The Code:
CGContextSaveGState(ctx);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 1.0);
CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, prevPoint.x, prevPoint.y);
for (NSValue *r in drawnPoints){
CGPoint pt = [r CGPointValue];
CGContextAddLineToPoint(ctx, pt.x, pt.y);
}
CGContextStrokePath(ctx);
CGContextRestoreGState(ctx);
I see you are using app in full screen mode where the view is centered and does not take full width of the screen.
It may be that CALayer has transform applied to it that translates the drawing from the left side of the screen to the center. This may not be the case with drawRect:. Try setting CGContext's transform matrix:
CGContextSaveGState(ctx);
CGFloat xOffset = CGRectGetMidX(screenFrame) - CGRectGetMidX(viewFrame);
CGContextTranslateCTM(ctx, xOffset, 0.0f);
// rest of drawing code
// ...
CGContextRestoreGState(ctx);