
Hi!
Tavmjonh Bah has suggested me to contact you and consult SPPaintServer class refactoring I would like to do.
Currently SPPaintServer contains pattern_new method. Since it directly handles rendering and uses cairo, I think it could be moved outside of SP tree.
My idea is to implement paint server drawing routines in a parallel hierarchy of classes belonging to New Renderer. Image [1] contains the class diagram. Common base for them would be NRPaintServer, containing pure virtual method pattern_new. Concrete NRPaintServer derivatives would contain a reference to corresponding SPTree items and use it while rendering.
To choose an appropriate NRPaintServer for given SPPaintServer I thought about a factory method in NRPaintServer class. Example interfaces and their usage are shown at [2]
The tricky part can be the implementation of NRPaintServer::create method. I have thought about two solutions: a) using a battery of dynamic_cast if statements like this: NRPaintServer* NRPaintServer::create(SPPaintServer* ps) { SPPattern* pattern = dynamic_cast<SPPattern*>ps; if (pattern != 0) { return new NRPattern(&pattern); } //and so on for gradients }
b) using double-dispatch strategy. There would be an .cpp file internal class NRPaintServerFactory. class NRPaintServerFactory { public: virtual NRPaintServer* create(SPPaintServer*); virtual NRPaintServer* create(SPLinearGradient*); virtual NRPaintServer* create(SPRadialGradient*); //and so on }
And NRPaintServer::create implementation would look like this: NRPaintServer* NRPaintServer::create(SPPaintServer* ps) { static NRPaintServerFactory *factory = new NRPaintServerFactory; return factory->create(ps); }
What are your thoughts about this solution?
[1]https://www.dropbox.com/s/bz83xd0ks378qys/NRPaintServer.svg [2]http://pastebin.com/h9AcrusW
Regards, Tomasz

2014-05-26 18:01 GMT+02:00 Tomasz Boczkowski <penginsbacon@...400...>:
Hi!
Tavmjonh Bah has suggested me to contact you and consult SPPaintServer class refactoring I would like to do.
Currently SPPaintServer contains pattern_new method. Since it directly handles rendering and uses cairo, I think it could be moved outside of SP tree.
My idea is to implement paint server drawing routines in a parallel hierarchy of classes belonging to New Renderer. Image [1] contains the class diagram. Common base for them would be NRPaintServer, containing pure virtual method pattern_new. Concrete NRPaintServer derivatives would contain a reference to corresponding SPTree items and use it while rendering.
This is not the correct solution - see below. The current design of pattern rendering is broken and needs to be significantly changed, instead of transferring this brokenness to the display (NR) tree
To choose an appropriate NRPaintServer for given SPPaintServer I thought about a factory method in NRPaintServer class. Example interfaces and their usage are shown at [2]
The tricky part can be the implementation of NRPaintServer::create method. I have thought about two solutions: a) using a battery of dynamic_cast if statements like this: NRPaintServer* NRPaintServer::create(SPPaintServer* ps) { SPPattern* pattern = dynamic_cast<SPPattern*>ps; if (pattern != 0) { return new NRPattern(&pattern); } //and so on for gradients }
This design has a major problem: it introduces a dependency of the display tree on the SP tree. Until now, the display tree had no awareness of the SP tree beyond storing a few opaque pointers.
b) using double-dispatch strategy. There would be an .cpp file internal class NRPaintServerFactory. class NRPaintServerFactory { public: virtual NRPaintServer* create(SPPaintServer*); virtual NRPaintServer* create(SPLinearGradient*); virtual NRPaintServer* create(SPRadialGradient*); //and so on }
And NRPaintServer::create implementation would look like this: NRPaintServer* NRPaintServer::create(SPPaintServer* ps) { static NRPaintServerFactory *factory = new NRPaintServerFactory; return factory->create(ps); }
This suffers from the same problem.
The correct solution is not to copy the SP tree hierarchy to the display tree, but to add patterns as another kind of child node, just like it is done for masks and clipping paths. This will also fix some performance problems we have with large bitmaps inside patterns. I will provide more details later this week.
Regards, Krzysztof

Hi,
I just have a few minor follow-ups on code implementation (not approach, as this was not the general solution for the specific problem at hand. I mainly wanted to point out a few of the approaches to coding to keep in mind (especially as our existing codebase is full of legacy code and non-best-practices)
On Mon, May 26, 2014, at 09:01 AM, Tomasz Boczkowski wrote:
The tricky part can be the implementation of NRPaintServer::create method. I have thought about two solutions: a) using a battery of dynamic_cast if statements like this: NRPaintServer* NRPaintServer::create(SPPaintServer* ps) { SPPattern* pattern = dynamic_cast<SPPattern*>ps; if (pattern != 0) { return new NRPattern(&pattern); } //and so on for gradients }
In general I dislike overuse of dynamic_cast<>. Sometimes it's helpful, but in other cases exposing an enum on the base class to denote the 'general type' of the specific subclass can help. Among other things it makes the expected behavior a little more visible, allows for use in switch statements, executes faster, etc. In this context I think it would not be a good solution, however.
b) using double-dispatch strategy. There would be an .cpp file internal class NRPaintServerFactory. class NRPaintServerFactory { public: virtual NRPaintServer* create(SPPaintServer*); virtual NRPaintServer* create(SPLinearGradient*); virtual NRPaintServer* create(SPRadialGradient*); //and so on }
That API is very "C-ish". A more C++ approach is to avoid pointers, add const-correctness, etc.
class NRPaintServerFactory {
public:
virtual NRPaintServer* create(SPPaintServer const &server) const;
virtual NRPaintServer* create(SPLinearGradient const &server) const;
virtual NRPaintServer* create(SPRadialGradient const &server) const;
//and so on
Notice also that I did include the name for the parameter, even though technically it is not required. Good names help add to maintainability of code.
But again, this is just using your example code to have concrete instances to discuss parameters, etc. As Krzysztof has mentioned, a different higher-level approach should be used in this specific case.
-- Jon A. Cruz jon@...18...
participants (3)
-
Jon A. Cruz
-
Krzysztof Kosiński
-
Tomasz Boczkowski