Re: Pixel-aligned autoresizing

2011-07-06 Thread Fred Kiefer

On 05.07.2011 22:15, Eric Wasylishen wrote:

Hi,

In the last month I was looking in to making GS's drawing output
match cocoa exactly, in particular, getting rid of rounding in the
backends.

One of the problems I encountered is GS can give autoresizing views
fractional positions/sizes, whereas when Cocoa autoresizes views it
only ever sizes them to pixel-aligned rects. The problem with GS's
current behaviour is it will make UI elements blurry if we get rid of
rounding in the backend.

Attached is a preliminary patch* which switches to Cocoa's behaviour.
I wanted to ask for feedback as to whether this is a reasonable patch
to apply.

The patch has to do a bit more than simply adding rounding to -
(void) resizeWithOldSuperviewSize: (NSSize)oldSize, because if we
just round _frame, we will lose track of where a view originally was.
I add a new ivar _autoresizingFrameError which stores the difference
between the rounded _frame, and what _frame would be without
rounding. The only slightly messy bit of the patch is that any parts
of the code that modify _frame directly should also set
_autoresizingFrameError to {0, 0}.


One result of this patch will be that calling -frame after a call to
-setFrame: with fractional coordinates will return a different result
than the one we provided. I just checked on Cocoa and as expected this
isn't the case there. For this reason I am strongly against this patch,
although I fully support the goal you are aiming at. We need to come up
with a different way for it.
Basically wee need to ensure that all drawing operations that are
initiated by the gui drawing code get pixel aligned. Somebody will have
to test whether this is true for all drawing operations, that is, when I
use an NSBezierPath with fractional coordinates to draw into a NSView,
will the result be blurred? I expect this to be the case, which means 
that we cannot put the pixel alignment directly into the backend, but 
need to do it in our own drawing code. The next level would be the 
functions in Functions.m. What will be the result if we feed them with 
fractional coordinates? And so on to determine the level of intervention 
that is needed.



[*] One thing I would change in the patch is instead of rounding
using floor(), I would call -centerScanRect: so that the
pixel-alignment takes in to account the transformation matrix of the
current view.


Good point, this method seems to be better suited. Are you aware of the
warnings in that method?


___
Gnustep-dev mailing list
Gnustep-dev@gnu.org
https://lists.gnu.org/mailman/listinfo/gnustep-dev


Re: Pixel-aligned autoresizing

2011-07-06 Thread Jens Ayton
On Jul 6, 2011, at 10:32, Fred Kiefer wrote:
> 
> Basically wee need to ensure that all drawing operations that are
> initiated by the gui drawing code get pixel aligned. Somebody will have
> to test whether this is true for all drawing operations, that is, when I
> use an NSBezierPath with fractional coordinates to draw into a NSView,
> will the result be blurred?

Yes, Cocoa NSBezierPath supports subpixel positioning. In fact, the most common 
question from beginners is why one-pixel rectangles at integer coordinates are 
drawn two pixels wide, and this is the answer.

Developers writing custom views are expected to use -centerScanRect: and 
related coordinate transform functions to ensure grid-aligned drawing under 
transformation and UI scaling, and I assume the standard views do too.


-- 
Jens Ayton


___
Gnustep-dev mailing list
Gnustep-dev@gnu.org
https://lists.gnu.org/mailman/listinfo/gnustep-dev


Re: Pixel-aligned autoresizing

2011-07-06 Thread Eric Wasylishen
Hi Fred,

On 2011-07-06, at 2:32 AM, Fred Kiefer wrote:

> On 05.07.2011 22:15, Eric Wasylishen wrote:
>> Hi,
>> 
>> In the last month I was looking in to making GS's drawing output
>> match cocoa exactly, in particular, getting rid of rounding in the
>> backends.
>> 
>> One of the problems I encountered is GS can give autoresizing views
>> fractional positions/sizes, whereas when Cocoa autoresizes views it
>> only ever sizes them to pixel-aligned rects. The problem with GS's
>> current behaviour is it will make UI elements blurry if we get rid of
>> rounding in the backend.
>> 
>> Attached is a preliminary patch* which switches to Cocoa's behaviour.
>> I wanted to ask for feedback as to whether this is a reasonable patch
>> to apply.
>> 
>> The patch has to do a bit more than simply adding rounding to -
>> (void) resizeWithOldSuperviewSize: (NSSize)oldSize, because if we
>> just round _frame, we will lose track of where a view originally was.
>> I add a new ivar _autoresizingFrameError which stores the difference
>> between the rounded _frame, and what _frame would be without
>> rounding. The only slightly messy bit of the patch is that any parts
>> of the code that modify _frame directly should also set
>> _autoresizingFrameError to {0, 0}.
> 
> One result of this patch will be that calling -frame after a call to
> -setFrame: with fractional coordinates will return a different result
> than the one we provided.

Hm, that shouldn't be the case. -setFrame: still sets the _frame ivar to 
whatever it is passed, and -frame still returns _frame unmodified.

The only difference in -setFrame:'s behaviour (and all other methods that 
modified _frame directly) is that it will have the side effect of clearing the 
_autoresizingFrameError, but this isn't directly observable. The value of 
_autoresizingFrameError is only used inside -resizeWithOldSuperviewSize: to 
compute the new size.

> I just checked on Cocoa and as expected this
> isn't the case there. For this reason I am strongly against this patch,
> although I fully support the goal you are aiming at. We need to come up
> with a different way for it.

Assuming there isn't actually a problem I'm overlooking, the patch probably 
needs to add some extra documentation explaining what is going on :-)

> Basically wee need to ensure that all drawing operations that are
> initiated by the gui drawing code get pixel aligned. Somebody will have
> to test whether this is true for all drawing operations, that is, when I
> use an NSBezierPath with fractional coordinates to draw into a NSView,
> will the result be blurred? I expect this to be the case, which means that we 
> cannot put the pixel alignment directly into the backend, but need to do it 
> in our own drawing code. The next level would be the functions in 
> Functions.m. What will be the result if we feed them with fractional 
> coordinates? And so on to determine the level of intervention that is needed.

Agreed. Most of that work is already done in GSTest's PixelExactDrawing-test, 
and you can toggle between what GS is rendering and a screen captures from OS 
X. Drawing with NSBezierPath and fractional coordinates should produce blurred 
output. Same for all of the ops in Functions.m which I've checked.

> 
>> [*] One thing I would change in the patch is instead of rounding
>> using floor(), I would call -centerScanRect: so that the
>> pixel-alignment takes in to account the transformation matrix of the
>> current view.
> 
> Good point, this method seems to be better suited. Are you aware of the
> warnings in that method?

Yes. One thing we could do is simply return the passed rect unmodified if 
[[NSGraphicsContext currentContext] isDrawingToScreen] is NO. Not sure if Cocoa 
does that but it would make sense to, I think.

-Eric
___
Gnustep-dev mailing list
Gnustep-dev@gnu.org
https://lists.gnu.org/mailman/listinfo/gnustep-dev


Re: Pixel-aligned autoresizing

2011-07-06 Thread Fred Kiefer

On 06.07.2011 11:19, Eric Wasylishen wrote:

Hi Fred,

On 2011-07-06, at 2:32 AM, Fred Kiefer wrote:


On 05.07.2011 22:15, Eric Wasylishen wrote:

Hi,

In the last month I was looking in to making GS's drawing output
match cocoa exactly, in particular, getting rid of rounding in the
backends.

One of the problems I encountered is GS can give autoresizing views
fractional positions/sizes, whereas when Cocoa autoresizes views it
only ever sizes them to pixel-aligned rects. The problem with GS's
current behaviour is it will make UI elements blurry if we get rid of
rounding in the backend.

Attached is a preliminary patch* which switches to Cocoa's behaviour.
I wanted to ask for feedback as to whether this is a reasonable patch
to apply.

The patch has to do a bit more than simply adding rounding to -
(void) resizeWithOldSuperviewSize: (NSSize)oldSize, because if we
just round _frame, we will lose track of where a view originally was.
I add a new ivar _autoresizingFrameError which stores the difference
between the rounded _frame, and what _frame would be without
rounding. The only slightly messy bit of the patch is that any parts
of the code that modify _frame directly should also set
_autoresizingFrameError to {0, 0}.


One result of this patch will be that calling -frame after a call to
-setFrame: with fractional coordinates will return a different result
than the one we provided.


Hm, that shouldn't be the case. -setFrame: still sets the _frame ivar to 
whatever it is passed, and -frame still returns _frame unmodified.

The only difference in -setFrame:'s behaviour (and all other methods that 
modified _frame directly) is that it will have the side effect of clearing the 
_autoresizingFrameError, but this isn't directly observable. The value of 
_autoresizingFrameError is only used inside -resizeWithOldSuperviewSize: to 
compute the new size.


Sorry, I was completely unclear in my previous mail. What I wanted to 
say was that after setFrame: you may get back a different frame although 
the only thing that happened was a call to super views setFrame: method 
with almost identical values as it had before.
What I didn't realize was that this will get worse, when a rotation is 
involved. The method -convertRectToBase: will return a much bigger 
rectangle in that case and the call to -convertRectFromBase: will only 
make it worse.



I just checked on Cocoa and as expected this
isn't the case there. For this reason I am strongly against this patch,
although I fully support the goal you are aiming at. We need to come up
with a different way for it.


Assuming there isn't actually a problem I'm overlooking, the patch probably 
needs to add some extra documentation explaining what is going on :-)


Basically wee need to ensure that all drawing operations that are
initiated by the gui drawing code get pixel aligned. Somebody will have
to test whether this is true for all drawing operations, that is, when I
use an NSBezierPath with fractional coordinates to draw into a NSView,
will the result be blurred? I expect this to be the case, which means that we 
cannot put the pixel alignment directly into the backend, but need to do it in 
our own drawing code. The next level would be the functions in Functions.m. 
What will be the result if we feed them with fractional coordinates? And so on 
to determine the level of intervention that is needed.


Agreed. Most of that work is already done in GSTest's PixelExactDrawing-test, 
and you can toggle between what GS is rendering and a screen captures from OS 
X. Drawing with NSBezierPath and fractional coordinates should produce blurred 
output. Same for all of the ops in Functions.m which I've checked.


Thank you, I had forgotten about these new test cases. I will have to 
recompile GSTest to see it.




[*] One thing I would change in the patch is instead of rounding
using floor(), I would call -centerScanRect: so that the
pixel-alignment takes in to account the transformation matrix of the
current view.


Good point, this method seems to be better suited. Are you aware of the
warnings in that method?


Yes. One thing we could do is simply return the passed rect unmodified if 
[[NSGraphicsContext currentContext] isDrawingToScreen] is NO. Not sure if Cocoa 
does that but it would make sense to, I think.


That would do.

___
Gnustep-dev mailing list
Gnustep-dev@gnu.org
https://lists.gnu.org/mailman/listinfo/gnustep-dev


Re: NSImage drawing (was Re: Problem with Gorm)

2011-07-06 Thread Quentin Mathé
Hi Eric and Fred,

Le 4 juil. 2011 à 23:07, Eric Wasylishen a écrit :

>> I had a quick look into these new methods and didn't like too much what I 
>> saw there.
> 
> I agree. It's a real mess.
> 
> The positive thing is that most of the rest of NSImage is pretty tidy, and 
> all drawing goes to -drawInRect:..., so we just need to fix that one method 
> :-).

Yes. I remember I had bad experiments when I started to touch this code, so I 
let it as is.

>> We have a lot of code duplication between these two methods, but we also 
>> have differences there which I don't understand. Why do we use scaling for 
>> the native drawing but not for the gui drawing?
> 
> Not sure, what do you mean by scaling? btw, my ImageSelectionTest in GSTest 
> currently lacks a test of the effect of setting a transformation matrix / a 
> NSView bounds transform before drawing an image, and that needs to be added. 
> The test should be renamed to ImageDrawing-test since it's turning in to a 
> general test of image drawing.

It's going to be hard to completely merge the two code paths, because the new 
code path implies the backend supports negative scaling (that's mostly why the 
gui drawing doesn't use scaling). Xlib don't support that. For such a backend, 
it means NSImage has to approximate the flipping result and ask the backend to 
draw the image in a custom location, rather than the one based on the current 
coordinates and CTM.

The Art backend could be corrected, but the code is very convoluted, so I have 
no idea whether it's possible without a major rewrite or not. I also tried to 
fix the GDI backend to use the new code path, because this backend code is much 
easier to understand, but I gave up because it wasn't so easy in the end. I 
don't recall which problems I encountered exactly.

It's important to remember the gui drawing works around various bugs in Xlib, 
GDI and Art backends, and these bugs are not the same in each backend… which 
makes rewriting this gui drawing quite tricky.

As a side note, I'm not sure there is a real interest in supporting the Art 
backend in the next GNUstep releases. Unless I overlook some real use case, I'd 
suggest to deprecate it. We have Cairo currently which does the same but much 
better. It looks to me that nowadays people uses either Cairo, GDI or Xlib.

For the record, here is the bug report where I introduced the two code paths 
and below I pasted what I wrote about these code paths:

https://savannah.gnu.org/bugs/?27782

> For -drawInRect:, we have now two code paths. The old one that handles the 
> scaling in Gui but has several issues: 
> - draw at the wrong location in flipped coordinates 
> - draw at the wrong location when there is a scaling with inRect and fromRect 
> - is slow when you want to draw a small image portion with a scaling or 
> rotation involved. 
> 
> And a new Cairo-specific code path much simpler that corrects all the 
> previous issues. 
> 
> Ultimately it is probably possible to reimplement -compositeGState on top of 
> -drawGState, but since the semantic differences are pretty important I don't 
> think it really matters that much. It was also easier to debug and to figure 
> out where the bugs really were once I introduced a distinct code path for 
> -drawInRect. 
> It's probably far from ideal, but when you consider the number of issues (or 
> changes required in each backend) and the fact we'll probably switch to Opal 
> in the long run, it sounds acceptable to me. 

Cheers,
Quentin.


___
Gnustep-dev mailing list
Gnustep-dev@gnu.org
https://lists.gnu.org/mailman/listinfo/gnustep-dev