[Drawkit] Fix for aliasing of drawing edges

Brad Larson larson at sonoplot.com
Tue May 20 10:32:36 PDT 2008


On May 20, 2008, at 1:14 AM, Graham Cox wrote:

> This gives somewhat more consistent results:
>
> 	if([NSGraphicsContext currentContextDrawingToScreen])
> 	{
> 		float factor = 0.5/[aView scale];
> 		
> 		NSAffineTransform* pixelShiftToAdjustForQuartzCoordinates =  
> [NSAffineTransform transform];
> 		[pixelShiftToAdjustForQuartzCoordinates translateXBy:factor  
> yBy:factor];
> 		[pixelShiftToAdjustForQuartzCoordinates concat];
>
> 		[[self paperColour] set];
> 		NSRectFill( rect );
> 	}
>
> There are still some occasional pixels left unpainted when scrolling  
> at high zooms, but far less, because here the actual zoom is used to  
> compute the necessary shift, which at high zooms is a much smaller  
> factor (it's still 0.5 of a pixel on screen, but now represents some  
> much smaller factor in the drawing).

Yes, I didn't test at higher zoom factors than 1.0.  I guess it didn't  
look so good.  Your correction seems to work well.  I came across some  
discussion about this back in one of the Cocoa mailing lists: http://www.cocoabuilder.com/archive/message/cocoa/2004/11/4/120821 
  .

>
>
> I had a look at the grid.
>
> The grid would need to employ a similar trick to avoid the squares  
> becoming highly uneven at large zoom factors - just flooring and  
> adding 0.5 causes a big shift in some of the lines which is very  
> obvious when you zoom in. The visual alignment of objects is thus  
> substantially impaired.
>
> At present, the grid is not recomputed for every redraw of the  
> screen, but is calculated once and cached. Without this caching  
> drawing the grid is a big performance hit (as it stands it barely  
> makes an impact). Thus the cache would have to be invalidated when  
> the view scale changes or else the cache removed and the grid  
> recalculated on every draw (bear in mind there could be multiple  
> views with differing scales rendering the same drawing). To my mind  
> this is just not worthwhile. By ignoring screen alignment issues,  
> DrawKit is accurate to the full floating-point resolution (the only  
> reason that turning off the grid crisps up the edges with the offset  
> applied is because the mouse itself only resolves to integer  
> coordinates). One benefit of Quartz is that this compromise is no  
> longer needed, with the small drawback that you get softer edges on  
> the screen because it's rendering floating-point objects into a  
> coarse grid of pixels.
>
> I guess if your focus is on-screen presentation, the offset trick is  
> worth using, but if it's printed output and geometric fidelity, it  
> isn't.
>
> Not sure how to resolve this - making it a switchable flag is one  
> option, but addressing it for the grid case is going to a) be  
> complicated and b) hit performance badly.

You could re-pixel-align and recache the grid on a drawing scale  
change.  It would make for a slower redraw on a zoom operation, but  
would it be slow enough to bother the user?  They may not change the  
scale very often.  The cached grid would still redraw quickly on a  
standard drawRect: operation.  To do this, could the grid layer  
respond to the kGCDrawingViewDidChangeScale notification sent out by  
the GCZoomView in the zoomViewByFactor:andCentrePoint: method?  Does  
the scaleUnitSquareToSize: NSView method used in GCZoomView to do the  
scaling trigger other redraw or recaching actions elsewhere?

If you really wanted high performance, but were willing to sacrifice  
backwards compatibility, layer-backing the drawing objects and grid  
through Core Animation should make refreshes very fast.  This would be  
great for dragging around lots of selected objects, where they all  
could be assigned to the same CALayer and that layer cached on the  
video card.  You could also do animated zooms, glowing shadows on  
selected objects, and other slick effects.  It would take a bit of  
work, however, and I don't know how much of a priority it is.   
Rendering performance is very good as it is right now.

>
> On 20 May 2008, at 2:01 pm, Graham Cox wrote:
>
>> I'm aware of the 0.5 pixel "problem", but I chose not to attempt to  
>> compensate for it, instead deciding to go for accuracy over  
>> crispness.
>>
>> That said, It definitely does look a lot sharper. One problem with  
>> the code below is that it causes a big problem when the view is  
>> scrolled when it's zoomed in to large scale. Not sure why at the  
>> moment, but it's so bad I can't recommend including this at  
>> present. I think the update area marking needs to be similarly  
>> offset otherwise there is a small amount of overlap that doesn't  
>> get repainted when scrolling.
>>
>> Regarding offsetting the grid alignment, I'll look into it, but one  
>> thing I do want to avoid is the appearance of uneven spacing  
>> between grid lines by forcing them to integer co-ordinates, and any  
>> misalignment of objects visually between the grid "real" position  
>> and the apparent position of the lines. This might not turn out to  
>> be a problem, just thinking aloud at this stage.
>>
>>
>> cheers, Graham
>>
>>
>>
>> On 20 May 2008, at 9:03 am, Brad Larson wrote:
>>
>>> I noticed that the borders of the drawing, along with the edges of  
>>> objects, were being aliased (one-pixel-wide black lines were drawn  
>>> as two-pixel-wide grey lines).  The Quartz drawing model is a  
>>> little odd in that integer coordinates are considered to be  
>>> between pixels, so you need to shift your drawing space by a half  
>>> pixel in X and Y to get things to line up correctly.
>>>
>>> By changing
>>>
>>> if([NSGraphicsContext currentContextDrawingToScreen])
>>> {
>>> 	[[self paperColour] set];
>>> 	NSRectFill( rect );
>>> }
>>>
>>> to be
>>>
>>> if([NSGraphicsContext currentContextDrawingToScreen])
>>> {
>>> 	NSAffineTransform* pixelShiftToAdjustForQuartzCoordinates =  
>>> [NSAffineTransform transform];
>>> 	[pixelShiftToAdjustForQuartzCoordinates translateXBy:0.5 yBy:0.5];
>>> 	[pixelShiftToAdjustForQuartzCoordinates concat];
>>> 		
>>> 	[[self paperColour] set];
>>> 	NSRectFill( rect );
>>> }
>>>
>>> in - (void) drawRect:inView: within DKDrawing.m, you can remove  
>>> this aliasing.  There is still some aliasing with the background  
>>> grids and objects drawn on them, due to their drawing coordinates  
>>> not mapping to integer values.  In DrawDemo, you'll need to turn  
>>> off grid snapping to see how much sharper rectangles and the like  
>>> are with this setting, because otherwise they'll snap to the non- 
>>> integer grid locations.
>>>
>>> A go-between function, template, or method might be needed to  
>>> floor() or ceil() the x and y values of grids and drawable objects  
>>> for display to the screen.  Apple has some tips on this at http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/chapter_4_section_6.html 
>>>  .
>>>
>>>
>>> ______________________
>>> Brad Larson
>>> SonoPlot, Inc.
>>> 3030 Laura Lane, Suite 120
>>> Middleton, WI 53562
>>>
>>>
>>>
>>> _______________________________________________
>>> Drawkit mailing list
>>> Drawkit at lists.apptree.net
>>> http://lists.apptree.net/listinfo.cgi/drawkit-apptree.net
>>
>> _______________________________________________
>> Drawkit mailing list
>> Drawkit at lists.apptree.net
>> http://lists.apptree.net/listinfo.cgi/drawkit-apptree.net
>
> _______________________________________________
> Drawkit mailing list
> Drawkit at lists.apptree.net
> http://lists.apptree.net/listinfo.cgi/drawkit-apptree.net

______________________
Brad Larson
SonoPlot, Inc.
3030 Laura Lane, Suite 120
Middleton, WI 53562





More information about the Drawkit mailing list