Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged Imagedraw rewrite #737

Merged
merged 25 commits into from
Jul 1, 2014

Conversation

wiredfool
Copy link
Member

Continuation of #610, merged to master.

Terseus and others added 19 commits April 1, 2014 13:01
The functions `polygon8`, `polygon32` and `polygon32rgba` all have exactly
the same logic in code, only changes the hline function called inside.

Now all the logic is contained in `polygon_generic` which gets a
function pointer to the hline handler, so there is no duplicated code
anymore.
The rounds used in the library are towards positive/negative infinity.
Since we're in a cartesian plane, rounds around zero are much more
convenient, so we can use positive and negative values with consistent
results.
The (previously refactored) polygon_generic function didn't draw
consistent polygons (equilateral polygons were not equilateral nor
symmetrical).

The most notable changes are:
  * The horizontal edges are searched for when finding the polygon
    boundaries, drawn and discarded from the edge list used to detect
    intersections.
  * The intersections are now checked and calculated from the current value
    of the scan line (ymin) instead of in the middle (ymin + 0.5).
  * Because the change in the scan line behavior, we should duplicate
    the intersections in the maximum Y value of an edge or there will be
    draw errors with concave and complex polygons.
  * The rounds of the X coordinates in the hline function calls are
    switched to draw the inner pixels.
  * Removed the ugly micro-optimization of qsort at the end.

This implementation of the scan line algorithm may not be technically
correct, it's not optimized and it have problems with some edge cases,
like a wide line from (x0, y) to (x1, y + 1), therefore it should be
reviewed in the future.
The previous version of the function didn't generate correct wide lines
of even width.

The most notable changes are:
  * Make variable names far more descriptive about the process.
  * Corrected the width calculation; we should deduct one pixel from the
    width because the pixels at the center of the line doesn't count for
    the triangles.
  * Now we calculate *two* ratios, one for left/top displacement (dxmin)
    and one for right/bottom (dxmax), this fix the behavior with lines
    of even width.

It can probably be optimized.
The method `create_base_image_draw` will be used in all the new draw
tests.
The diagonals of the right angled edges must be perfect and the bottom
vertice should be drawn.
Notice that the expansion of the line width depends on the order of the
points:
  * If the bigger axis value is provided as the *second* point the line
    expand first to the *positive* side of the axis.
  * If the bigger axis value is provided as the *first* point the line
    expand first to the *negative* side of the axis.
  * If the line's width is odd this doesn't matter, as the line will
    expand the same amount to both sides.

This behavior should be consistent in both horizontal and vertical lines.
The behavior is consistent with horizontal lines, see previous commit
for details.
Now writing in the wall "don't push before compile" 100 times.
Only the oblique 3 pixels wide lines are defined:
* The oblique 2 pixels wide lines are somewhat hard to define.
* To define the oblique lines wider than 3 pixels we neet to define
  first how the oblique lines should expand their width (realistic or
  exact).
These tests should guarantee that the proportion of the width is
maintained with a margin of error < 1%.
This tests are designed to guarantee that the wide lines behave exactly
like normal lines drawn with the Bresenham's algorithm.
This tests are somewhat subjective since this is non-defined behavior,
but I think that mimic the Bresenham's algorithm is reliable enough.

Currently the horizontal version of this test **fail**.
@wiredfool
Copy link
Member Author

Remaining failure is the "line horizontal 1px slope 2px wide" test, which was mentioned in the original as an inconsistency between horizontal and vertical lines.

@hugovk
Copy link
Member

hugovk commented Jun 30, 2014

The pieslice and polygon test images look immediately better, no more rogue pixels outside the border.

@wiredfool
Copy link
Member Author

I've now skipped the one failing test, I'll re-enable in a new PR/bug to fix that one behavior.

IMO, this is an overall win for us, despite the failing test. I recommend merge before release.

@hugovk
Copy link
Member

hugovk commented Jul 1, 2014

The 3.4 build fails when building draw.c:

libImaging/Draw.c: In function ‘polygon_generic’:
libImaging/Draw.c:429:5: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
libImaging/Draw.c:433:5: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
libImaging/Draw.c:460:5: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
libImaging/Draw.c: In function ‘ImagingDrawWideLine’:
libImaging/Draw.c:602:5: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
libImaging/Draw.c:609:5: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
cc1: some warnings being treated as errors

@wiredfool
Copy link
Member Author

Gah. I missed that with the other test failures. I'll fix. That will kill windows as well.

It doesn't do that OMM.

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 98a4991 on wiredfool:terseus_imagedraw into * on python-pillow:master*.

aclark4life added a commit that referenced this pull request Jul 1, 2014
@aclark4life aclark4life merged commit b2ed31e into python-pillow:master Jul 1, 2014
@Terseus
Copy link
Contributor

Terseus commented Jul 9, 2014

Oops, sorry for coming too late, I didn't though this would be merged in its current state.
After some more research on this subject I've realized that my implementation was wrong.

Long story short, the one big rule when drawing polygons is, every pixel belong to one, and only one, polygon.
Now what happens if we draw two squares, one next to the other? If we count all the edges as part of its polygon and therefore draw them, like my implementation does, the two squares will overlap in the pixel where the edges are touching.
That's the reason why the bottom and right edges of a polygon shouldn't be drawn, and I think that the original implementation tried to do it in this way (but failed in doing it correctly).

I'll work in a new implementation of the polygon draw algorithm this week, sine this is now merged into mainline I'll send a new PR with it along with the tests adapted to the correct behavior.

If someone wants to know more about this subject I recommend beginning with this article of gameprogrammer.com, although it's dated it's also one of the few articles I found which explain the most important aspects of polygon rendering.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants