Bug 103795 - [PATCH] Implement ArthurOutputDev::axialShadedFill
Summary: [PATCH] Implement ArthurOutputDev::axialShadedFill
Status: RESOLVED FIXED
Alias: None
Product: poppler
Classification: Unclassified
Component: qt frontend (show other bugs)
Version: unspecified
Hardware: Other All
: medium normal
Assignee: poppler-bugs
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-11-17 16:09 UTC by oliver.sander
Modified: 2018-03-27 22:02 UTC (History)
1 user (show)

See Also:
i915 platform:
i915 features:


Attachments
Patch implementing ArthurOutputDev::axialShadedFill (7.23 KB, patch)
2017-11-17 16:09 UTC, oliver.sander
Details | Splinter Review
wrongly colored shading regression (1.27 MB, application/pdf)
2018-01-03 23:08 UTC, Albert Astals Cid
Details
Updated patch that fixes the color problem (7.35 KB, patch)
2018-03-09 20:47 UTC, oliver.sander
Details | Splinter Review

Description oliver.sander 2017-11-17 16:09:46 UTC
Created attachment 135558 [details] [review]
Patch implementing ArthurOutputDev::axialShadedFill

The attached patch implements ArthurOutputDev::axialShadedFill. This does not improve the rendering quality much, because the fall-back approximation code in Gfx does a pretty good job at it.  However, the new code

- Should be faster
- Does improve rendering a little bit: Previously, axial gradients showed spurious thin white lines when rendered with the arthur backend
- Is a warm-up exercise for radial gradients, which do look strange when rendered with Arthur.

Unfortunately I only have a few documents with axial gradients.  Some extra testing would be appreciated!

Unlike CairoOutputDev the Arthur implementation does not use the updateFillColorStop mechanism, but rather implements the entire QLinearGradient setup and painting in the axialShadedFill method.  This makes the code a bit longer, but:
    
- It avoids having to add additional internal state to the
  ArthurOutputDev class. This makes debugging easier.
- Besides computing color stops, the method Gfx::doAxialShFill
  (which is the one that calls updateFillColorStop) does many
  other things that are not actually needed when using Qt for
  painting the gradient.  In particular, it constructs a sequence
  of small rectangles to approximate the gradient, which are
  then never used.
- Gfx::doAxialShFill assumes that the output devices can only paint
  polygons with a single color.  Therefore it needs to construct
  lots of such polygons to approximate a smooth gradient.
  However, QPainter can do linear interpolation of colors
  itself.  The new bisection implementation in Arthur::axialShadedFill
  takes this into account, and therefore produces a much smaller
  number of color stops in many cases.
Comment 1 Albert Astals Cid 2017-11-17 22:11:18 UTC
Want me to run this over the files i have here to spot any big regression or should i just commit it?
Comment 2 oliver.sander 2017-11-18 07:37:34 UTC
Please run this over your files.  Thank you!
Comment 3 Albert Astals Cid 2017-11-20 23:21:11 UTC
doesn't compile, does this need something else?

/home/tsdgeos/devel/poppler/qt5/src/ArthurOutputDev.cc:750:13: error: request for member ‘top’ in ‘((ArthurOutputDev*)this)->ArthurOutputDev::m_painter’, which is of pointer type ‘QPainter*’ (maybe you meant to use ‘->’ ?)
   m_painter.top()->fillPath( convertPath( state, state->getPath(), Qt::WindingFill ), newBrush );
Comment 4 oliver.sander 2017-11-21 11:47:45 UTC
Apologies---you will need the patches from

  https://bugs.freedesktop.org/show_bug.cgi?id=102036

for this to compile.  Somehow I was expecting you to review my patches in the order of submission. :-)

If you want I can rebase this patch to the master.  Or better: can you run the regression tests with the patches from 102036 and this one together?  I don't really expect 102036 to introduce regressions, but extra testing certainly would not hurt.
Comment 5 Albert Astals Cid 2018-01-03 23:07:59 UTC
Have a look at the attached file, the gradient is fixed (i.e. you can't see the X anymore), but it's purple instead of green.
Comment 6 Albert Astals Cid 2018-01-03 23:08:40 UTC
Created attachment 136535 [details]
wrongly colored shading regression
Comment 7 oliver.sander 2018-01-08 10:47:30 UTC
I'll have a look, but it will have to wait until february.
Comment 8 oliver.sander 2018-03-09 20:47:10 UTC
Created attachment 137943 [details] [review]
Updated patch that fixes the color problem
Comment 9 oliver.sander 2018-03-09 22:03:53 UTC
I also figured out why the other shadings in that document look bad.  They are all drawn by the default shader in Gfx.cc, which calls the actual output device only for drawing lots of small unicolor polygons.  That code does the right thing.  However, Arthur draws those polygons with antialiasing enabled, which makes them a tiny bit smaller.  As a result, you get thin transparent lines around these polygons, and these are the artifacts you see.  If I switch off antialiasing, then the test document is rendered correctly.

The problem now is that I don't want to switch off antialiasing unconditionally.  I want to switch it off when the shading operation begins, and restore it afterwards.  However, there does not seem to be a hook that gets called after a shading has completed.  Any ideas?
Comment 10 Albert Astals Cid 2018-03-26 20:19:20 UTC
Which kind of shading it is?

I can see two solutions:
 * Add pre/post function calls in gfx.cc before the shading is done
 * Implemnt the shading in arthuroutputdev, but make the implementation just be
disableAA();
doWhateverTheGfxCodeDoes();
enableAA();
Comment 11 oliver.sander 2018-03-27 08:58:05 UTC
> Which kind of shading it is?

I tried it with all those from the example document you posted in Comment 6.

But it seems that I have to revisit my previous comment a little.  I am simply experiment using the following hack:

--- a/qt5/src/ArthurOutputDev.cc
+++ b/qt5/src/ArthurOutputDev.cc
@@ -777,6 +777,7 @@ void ArthurOutputDev::stroke(GfxState *state)
 
 void ArthurOutputDev::fill(GfxState *state)
 {
+  m_painter.top()->setRenderHint(QPainter::Antialiasing,false);
   m_painter.top()->fillPath( convertPath( state, state->getPath(), Qt::WindingFill ), m_currentBrush );
 }

But I just learned that this only works if the resolution we are rendering to is high enough (using test-render-to-file-qt5).  So this needs more thought.

> I can see two solutions:
>  * Add pre/post function calls in gfx.cc before the shading is done

That would be easy, but it would mean a new API in OutputDev.h.  It's not on me to decide whether that is a good idea.

>  * Implemnt the shading in arthuroutputdev, but make the implementation just be
> disableAA();
> doWhateverTheGfxCodeDoes();
> enableAA();

Problematic, because doWhateverTheGfxCodeDoes() is a lot of code.
Comment 12 Albert Astals Cid 2018-03-27 22:01:54 UTC
(In reply to oliver.sander from comment #11)
> > Which kind of shading it is?
> 
> I tried it with all those from the example document you posted in Comment 6.
> 
> But it seems that I have to revisit my previous comment a little.  I am
> simply experiment using the following hack:
> 
> --- a/qt5/src/ArthurOutputDev.cc
> +++ b/qt5/src/ArthurOutputDev.cc
> @@ -777,6 +777,7 @@ void ArthurOutputDev::stroke(GfxState *state)
>  
>  void ArthurOutputDev::fill(GfxState *state)
>  {
> +  m_painter.top()->setRenderHint(QPainter::Antialiasing,false);
>    m_painter.top()->fillPath( convertPath( state, state->getPath(),
> Qt::WindingFill ), m_currentBrush );
>  }
> 
> But I just learned that this only works if the resolution we are rendering
> to is high enough (using test-render-to-file-qt5).  So this needs more
> thought.
> 
> > I can see two solutions:
> >  * Add pre/post function calls in gfx.cc before the shading is done
> 
> That would be easy, but it would mean a new API in OutputDev.h.  It's not on
> me to decide whether that is a good idea.

Do you think it would be a bad idea?

> 
> >  * Implemnt the shading in arthuroutputdev, but make the implementation just be
> > disableAA();
> > doWhateverTheGfxCodeDoes();
> > enableAA();
> 
> Problematic, because doWhateverTheGfxCodeDoes() is a lot of code.

We can put that "lot of code" in a function to allow for code reuse.
Comment 13 Albert Astals Cid 2018-03-27 22:02:18 UTC
I've pushed the patch here, let's continue the other discussion either in the mailing list or in a bug about that issue in itself?


Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.