Re: Avoid drawing in half pixel
Re: Avoid drawing in half pixel
- Subject: Re: Avoid drawing in half pixel
- From: Bill Hernandez <email@hidden>
- Date: Thu, 6 May 2010 09:00:05 -0500
Gustavo,
I ran into this a few days ago when I was drawing a variable number of rectangles to a view, and found that the spacing was not always exact as one would expect. In my case the gist of it was that <[each unit]> in essence was a <[spacer + rectangleWidth]>
someViewWidthRequired = <[spacer + rectangleWidth]> * <[number of units]> + <[endSpacer]>
unfortunately <[someViewWidthRequired]> is not always equal to <[someViewWidthAvailable]>
to figure out the initial rectangleWidth I used :
w = (rect.size.width - ((noOfBoxes + 1) * spacerX))/noOfBoxes;
Which if you convert :
(w * noOfBoxes) + ((noOfBoxes + 1) * spacerX) = rect.size.width;
rect.size.width = (w * noOfBoxes) + ((noOfBoxes * spacerX) + ( 1 * spacerX));
rect.size.width = (noOfBoxes * w) + (noOfBoxes * spacerX) + ( 1 * spacerX);
rect.size.width = (noOfBoxes * (w + spacerX)) + ( 1 * spacerX);
is the same as :
someViewWidthRequired = <[spacer + rectangleWidth]> * <[number of units]> + <[endSpacer]>
but unfortunately, when resizing the window, more often than not, there was a nonZero remainder.
So while looping through to draw the rectangles, for the last one in the inner loop I made an adjustment to the last rectangle, which is not noticeable, rather than have the last spacer (endSpacer) look bad...
if (this is the last rectangle in the inner loop, adjust its size so the spacer looks good)
{
w = (rect.size.width - ((noOfBoxes + 1) * spacerX) - (i * w));
}
You are welcome to see how I handled this little dilemma at :
http://discussions.apple.com/forum.jspa?forumID=728&start=0
The Topic:
TUTORIAL: NSView flexible dynamic creation and loading of text and images
The actual post :
http://discussions.apple.com/thread.jspa?threadID=2421615&tstart=0
or you can download the project at :
http://www.journey-of-flight.com
Topic:
Cocoa - Flexibility of Working with Views
Shows how to create Cocoa - Objective C window views, and how to dynamically display text and images...
Best Regards,
Bill Hernandez
Plano, Texas
This is a piece of the method that handles the drawing :
// +---------+---------+---------+---------+---------+---------+---------+---------+
- (void)drawRectDemo:(NSRect)rect
{
<snip>
. . .
. . .
</snip>
// +---------+---------+---------+---------+---------+---------+---------+---------+
// DEFINE AND INITIALIZE MAIN RECT VARIABLES
// +---------+---------+---------+---------+---------+---------+---------+---------+
NSUInteger i = 0; // init the inner loop (column) counter
NSUInteger j = 0; // init the outer loop (row) counter
NSUInteger counter = 1; // init the counter for the total number of items
NSRect boxRect; // declare a general purpose rectangle
CGFloat x, y, w, h; // declare {origin, size} integer variables
w = 99; // Init with bogus width, real width will be calculated below
w = (rect.size.width - ((noOfBoxes + 1) * spacerX))/noOfBoxes;
// +---------+---------+---------+---------+
// [5201] ( BEGIN ) outer loop (rows)
// +---------+---------+---------+---------+
for( j = 0; j < noOfRows; j++)
{
if(useDynamicHeight)
{
h = (rect.size.height - (heightUsedByHeader + ((noOfRows + 1) * spacerY)));
h = (h/noOfRows);
}
else
{
h = fixedBoxHeight; // if (useDynamicHeight == NO) {use fixed height}
}
// +---------+---------+---------+---------+---------+---------+---------+---------+
// var below is initialized, but not used for now, will possibly be used in future
// +---------+---------+---------+---------+---------+---------+---------+---------+
x = spacerX;
if(showHeaderBox)
{
y = heightUsedByHeader + ((j + 1 ) * spacerY) + (j * h); // leave (2 * spacerY) as fixed value
}
else
{
// These are both the same equation, but they are here in case
// you want to do something different if the header {isShown, isNotShown}
y = heightUsedByHeader + ((j + 1 ) * spacerY) + (j * h); // leave (2 * spacerY) as fixed value
}
// +---------+---------+---------+---------+
// [5202] ( BEGIN ) inner loop (columns)
// +---------+---------+---------+---------+
for( i = 0; i < noOfBoxes; i++)
{
x = (spacerX * (i + 1)) + (w * i);
if(adjustLastBoxForNonZeroRemainder)
{
if (i == (noOfBoxes - 1))
{
/*
// +---------+---------+---------+---------+---------+---------+---------+---------+
* Originally when I was trying to figure out how to do all this, I had used
* NSUInteger instead of CGFloat vars for the rectangles, and that
* caused an uneven last spacer.
*
* This solved that problem, and it actually worked very well.
* It was after I had figured out this little work-around that I realized that
* I didn't need this at all, by using CGGloats, Cocoa automatically took care
* of this self induced problem for me.
*
* Now that I have (corrected) changed the code :
*
* FROM :
*
* NSUInteger x, y, w, h; // declare {origin, size} as NSUInteger variables
* boxRect = NSMakeRect(x, y, w, h);
*
* TO :
*
* CGFloat x, y, w, h; // declare {origin, size} as CGFloat variables
* boxRect = NSMakeRect(x, y, w, h);
*
* The code below provides the solution, to my original bufoonery, and
* like I said before, it actually was a clever, but un-needed solution...
*
* Make an adjustment to the last box, so that the last spacer doesn't look bad.
* if there is a remainder, then adjust the width of the last box by one or more pixels,
* which is less noticeable than having the last spacer be off by one or more pixels
* The maximum possible error (number of pixels) at most, will equal dX = (noOfBoxes - 1);
// +---------+---------+---------+---------+---------+---------+---------+---------+
*/
w = (rect.size.width - ((noOfBoxes + 1) * spacerX) - (i * w));
}
}
boxRect = NSMakeRect(x, y, w, h);
NSLog(@"[4808] w = %.0f", w);
// NSDottedFrameRect(boxRect);
NSBezierPath *bp;
bp = [NSBezierPath bezierPathWithRect:boxRect];
if(useGradientBackGround == NO)
{
[[NSColor lightGrayColor] set]; // set the pen to gray
}
else
{
[borderColor set]; // set the pen to borderColor (which is a variable)
}
[bp stroke];
// +---------+---------+---------+---------+---------+---------+
// [5203] ( BEGIN ) THIS IS WHERE YOU LOAD EACH BOX
// +---------+---------+---------+---------+---------+---------+
// Instead of the dynamic message below, you could do something
// totally different.
// For example : you could have an array with images, text, etc.
// You could also hide all the checkboxes, popups, etc...
// +---------+---------+---------+---------+---------+---------+
<snip>
. . .
. . .
</snip>
// +---------+---------+---------+---------+---------+---------+
// [5203] ( _END_ ) THIS IS WHERE YOU LOAD EACH BOX
// +---------+---------+---------+---------+---------+---------+
}
// +---------+---------+---------+---------+
// [5202] ( _END_ ) inner loop (columns)
// +---------+---------+---------+---------+
}
// +---------+---------+---------+---------+
// [5201] ( _END_ ) outer loop (rows)
// +---------+---------+---------+---------+
}
// +---------+---------+---------+---------+---------+---------+---------+---------+
On May 5, 2010, at 10:18 AM, Gustavo Pizano wrote:
> Hello all.
>
> I have a split view and inside I have custom views, in one subview Im drawing lines with a NSBezierPath, and when I move the divider I see sometimes the lines very thin some others no, so I know this last behavior its because Im drawing in half a pixel.
>
> I was reading the apple docs about this topic, and I tried at the beginning of my drawRect method I did as in "Converting Coordinate Values" topic suggest to convert my starting drawing point, but that din't help. I still see the above behavior.
> I realize then that when I resize the window I have the same behavior, so what I did was in the windowDelegate windowWillResize method, I did the following:
>
> if((NSInteger)frameSize.width % 2 != 0){
> frameSize.width += 1.0f;
>
> }
> if((NSInteger)frameSize.height % 2 != 0){
> frameSize.height += 1.0f;
> }
>
> and when I resize the window I don't see hte problem anymore. I tried applying this same to the splitview delegate when resizing, but unfortunately this approach didn't work well, I started getting warnings in the console that the size of the subviews weren't correct and it was being recalculated at the cost of performance.
>
> So any other idea on what can I do to avoid this behavior?
>
> Thanks
>
> Gustavo
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden