NEW! Method for rasterizing polygons with ANTI-ALIASING!


#1

Its not often that a completely new algorithm for efficiently rendering anti-aliased polygons and bezier curves comes around the block, but HERE IT IS!!!

http://josiahmanson.com/research/wavelet_rasterization/


#2

Wow! Didn’t really think there could be any other way to do it… I don’t understand the mathematical squiggles in their paper, but the code looks pretty simple. Sadly they say that it’s 3x slower than AGG, so it’s probably also slower than my current code, but the quality looks great.


#3

3x slower yes but the authors admit their code is unoptimized. And on the plus side, the wavelet rasterization can be parallelized easily.

So on a related note, is the interface to the juce LowLevelSoftwareRenderer sufficient that this wavelet rasterization method could be swapped in programatically, at run time, without changing any Juce library code? If not, it should be!


#4

Very cool!

In the small world department, I know the guy’s professor, Scott Schaefer, slightly. Another of his students, who is now at Pixar, contacted me about a paper I did in the early 80’s. We had been doing animated vector graphics for laser projection on a 2MHz Z80 (Epcott Center, Stone Mountain, Yes, Pink Floyd, etc), using multiplying DACs cascaded together to do the actual matrix math in analog. Paul Rother had gone on to Digital Productions, an early player in Digital Imaging for film (2010, Last Starfighter, etc), but we teamed up again to do some special projects. On one of them used a screaming 8MHz Hitachi Z80 clone. Heady with all that power, we decided to do real time shape morphing. The scheme worked and I wrote it up for others.

But when the kid contacted me, I didn’t understand a word he said. I felt like a caveman gnawing on an antelope bone when a futuristic hovercraft swoops in, pops open, and a highly evolved future being sticks his head out and thanks you for your accidental discovery that dirt isn’t consume by fire…


#5

:lol:


#6

It’d be nice if we could swap in the AGG method too.


#7

It’d be nice if we could swap in the AGG method too.[/quote]

Why!? I’m sure my rasteriser is just as good as the AGG one!


#8

Why!? I’m sure my rasteriser is just as good as the AGG one![/quote]

I’m sorry to have confused things here - what I meant was I’d love to make use of the font rasterisation technique mentioned on the AGG site.

Your rasteriser is very good and fast. I very much like everything about it other than the lack of LCD optimised rasterisation.

I’ve been looking at your code for quite some time to see if I can make sense of it and update it myself, but it’s been a fitful effort and I fear my poor brain isn’t quite up to it! I’m currently grappling with the EdgeTable iterator trying to figure out how that works with the ClipRegion stuff. Any tips on how to compute the sub-pixel data would be greatly appreciated… :smiley:


#9

For LCD rendering I think you’d only need to worry about the edge table handling, not the clip regions. I’ve not thought about it in detail, but I guess that you’d just make the edge tables 3x wider than normal, and use the result as your R, G, B alphas, rather than a single alpha value.


#10

Making the EdgeTable 3x wider makes perfect sense and I think I see what you mean about the ClipRegions.

The renderers would need changing though. For instance, the SolidColour renderer is instantiated with the specific colour. It could only render R/G/B levels separately if either it’s given the 3x pixel data or it’s told what RGB value to use for each pixel. Either way, that’s a non-trivial change to the design.


#11

Yes, I guess there’d need to be an alternative edge table iterator that produces 3 alpha values per pixel rather than 1, and then each renderer would need to have an extra function or two to handle that.


#12

Sounds so simple when you say it like that.

I’m working through the EdgeTable class to make sure I understand the absolute level values correctly before I look at fiddling with that levelAccumulator in the iterator. Handling the first pixel of a segment makes sense, but I’m haven’t worked out how to manage the last pixel. I think I need to have a good sleep and let my subconscious work on it :slight_smile:

BTW Vinnie - sorry for hijacking your thread!


#13

There’s no hijack, because your post is directly related to a question I brought up that Jules cough conveniently ignored:


#14

Well… The answer’s no: the software engine certainly doesn’t have a hot-swappable rasteriser! And TBH I don’t agree that it should have one! I designed it to do its job as simply and cleanly as possible, not to make it easy to tinker with. Of course you could write a new LowLevelGraphicsContext to use a different algorithm.


#15

Actually…it already does! You added LookAndFeel::createGraphicsContext()! That’s what I meant…the choice of LowLevelGraphicsContext is no longer hard coded in the native ComponentPeer implementation (cheers). Although there are a few places that still use a hard-coded class, I posted about it.

I guess you could just make a copy of the Juce code and modify it. But you are SO CLOSE to making it customizable! Look how easy it is:

First put the entire SoftwareRendererClasses namespace into a header file, and turn it into a class:

juce_SoftwareRendererClasses.h

class SoftwareRendererClasses
{
public:
  // ...

Take LowLevelGraphicsSoftwareRenderer::SavedState out of LowLevelGraphicsSoftwareRenderer, rename it, turn it into a template and move it into its own header:

juce_SoftwareRendererSavedState.h

template <class TypeOfSoftwareRendererClassesToUse = SoftwareRendererClasses>
class SoftwareRendererSavedState
{
public:
    SavedState (const Image& image_, const Rectangle<int>& clip_)
        : image (image_), clip (new TypeOfSoftwareRendererClassesToUse ::ClipRegion_RectangleList (clip_)),
          transform (0, 0),
          interpolationQuality (Graphics::mediumResamplingQuality),
          transparencyLayerAlpha (1.0f)
    {
    }
    //...

Turn LowLevelGraphicsSoftwareRenderer into a template:

juce_LowLevelGraphicsSoftwareRenderer.h

template <class TypeOfSavedStateToUse = SoftwareRendererSavedState>
class JUCE_API  LowLevelGraphicsSoftwareRenderer : public LowLevelGraphicsContext
{
  //...
protected:
    RenderingHelpers::SavedStateStack<TypeOfSavedStateToUse> savedState;
//...

At this point the Juce end of it is done. To implement Wavelet rasterization, subclass SoftwareRendererClasses in your application:

WaveletSoftwareRendererClasses.h

class WaveletSoftwareRendererClasses : public SoftwareRendererClasses
{
public:
  template <class PixelType, bool replaceExisting = false>
  class SolidColourEdgeTableRenderer
  {
    // Implement wavelet rendering here
//...

Now add this to your application’s custom LookAndFeel:

    virtual LowLevelGraphicsContext* createGraphicsContext (const Image& imageToRenderOn,
                                                            const Point<int>& origin,
                                                            const RectangleList& initialClip)
    {
      return new LowLevelGraphicsSoftwareRenderer <SoftwareRendererSavedState <WaveletSoftwareRendererClasses > > (
         imageToRenderOn, origin, initialClip);
    }

See how easy that was!

This will FINALLY get everyone off your back about replacing parts of the software renderer can’t you just TASTE the freedom?


#16

Yep, good suggestion! Can you remind me about this again when I’ve got the next release out there - I’m trying to stabilise everything at the moment, don’t really want to shake things up too much right now.


#17

Here’s a reminder :slight_smile:


#18

Blimey, let me catch my breath!


#19

A curve to curve stroker would be useful as well.


#20

That would indeed be cool. Difficult though!