How do I set up a Quartz2D coordinate system with scaling to avoid fuzzy drawing?

This section has been scratched once or twice, but I'm still puzzled. And Google was not friendly either.

Since quartz allows the use of arbitrary coordinate systems using affine transformations, I want to be able to draw objects such as floor plans using a real coordinate, for example. legs.

Basically, for an example, I want to scale the view so that when I draw a 10x10 rectangle (like a 10-inch box), I get a 60x60 pixel rectangle.

This works, except that the rectangle I get is pretty fuzzy. Another question here got an answer that explains why. However, Iโ€™m not sure I understood this reason, and, moreover, I donโ€™t know how to fix it. Here is my code:

I set my coordinate system to my own awakeFromNib view awakeFromNib :

 - (void) awakeFromNib { CGAffineTransform scale = CGAffineTransformMakeScale(6.0, 6.0); self.transform = scale; } 

And here is my routine:

 - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGRect r = CGRectMake(10., 10., 10., 10.); CGFloat lineWidth = 1.0; CGContextStrokeRectWithWidth(context, r, lineWidth); } 

The square I get scaled just fine, but completely fuzzy. Playing with lineWidth does not help: when lineWidth set smaller, it becomes lighter, but not sharper.

So, is there a way to adjust the view to a scaled coordinate system so that I can use the coordinates of my domain? Or do I need to go back and implement scaling in my drawing procedures?

Please note that this problem does not occur for translation or rotation.

thanks

+5
iphone cocoa-touch cocoa quartz-2d
source share
2 answers

Well, as often, an explanation of the problem leads me to a solution.

The problem is that the view transform property is applied to it after it has been pulled into the bit buffer. The scaling transform must be applied before drawing, i.e. in the drawRect method. So, let's scratch the awakeFromNib I gave, and here is the correct drawRect:

 - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGAffineTransform scale = CGAffineTransformMakeScale(6.0, 6.0); CGContextConcatCTM(context, scale); CGRect r = CGRectMake(10., 10., 10., 10.); CGFloat lineWidth = 0.1; CGContextStrokeRectWithWidth(context, r, lineWidth); } 
+2
source share

The [stroked] rectangle I get is pretty fuzzy.

This is usually due to the fact that you built a rectangle on the coordinates of an integer, and your line width is 1.

In PostScript (and, therefore, in its descendants: AppKit, PDF, and Quartz), drawing units by default indicate points, 1 point - exactly 1/72 of an inch. Currently, Macs and iPhones treat each dot as 1 pixel, regardless of the actual resolution of the screen (s), so in a practical sense, the dots (by default on Macs and iPhones) are equal to pixels.

In PostScript and its descendants, the integral coordinates are between points. 0, 0, for example, is the lower left corner of the lower left point. 1, 0 - the lower right corner of the same point (and the lower left corner of the next point on the right).

The incident is centered along the path that you stroke. Thus, half will be inside the path, half outside.

In the (conceptually) world of 72 dpi Mac, these two facts combine to create a problem. If 1 pt is equal to 1 pixel, and you apply a 1-pt move between two pixels, then half the stroke will hit each of these pixels.

Quartz will at least do this by painting the current color at both pixels in half color alpha. He determines this based on how much of the pixel is covered by a conceptual touch; if you used a line width of 1.5 pt, half of which is 0.75 pt, which is three quarters of each pixel 1 pixel in size, so the color will be displayed in 0.75 alpha. This, of course, leads to the natural conclusion: if you use a 2-pt line width, each pixel is completely covered, so the alpha will be 1. That's why you can see this effect with a 1-pt stroke, not a 2-pt stroke .

There are several ways:

  • Halftone movement: exactly what it says on the box, you translate up and right to half a point, compensating for the aforementioned 1-pt-cut-in-half split.

    This works in simple cases, but disappears when you include any other coordinate transformations, with the exception of whole translations. That is, you can translate to 30, 20, and it will still work, but if you translate to 33 + 1/3, 25.252525 ... or if you scale or rotate at all, your half-point translation will be useless.

  • Inner move: First fasten, then double the width of the line (because you only draw half of it), then do a stroke.

    This may require juggling gstate if you have a lot of different artwork because you don't want this clipping path to affect your other artwork.

  • Outside: essentially the same as the inside, except that you cancel the path to clipping.

    It may be better (less gstate juggling) than the inner move if you are sure that the paths you want to stroke will not overlap. On the other hand, if you also want to fill in the path, gstate juggling returns.

* It will not last forever. Apple hinted for some time that at some point they would change at least the resolution for drawing a Mac. The core of the API for such a change is almost everything there is now; the thing is that Apple throws a switch.

+10
source share

All Articles