First of all, sorry for not making a blog post early on during the community bonding period. I couldn't because I was mostly busy with Krita's Android release.
Secondly, some of you might remember me from the previous year. I was GSoC student for Krita. Now it is my second time! :-)
Finally, to tell a bit about my project. My project is to add support for SVG mesh gradients in Krita and the relevant task is: https://phabricator.kde.org/T13101 and branch is: https://invent.kde.org/graphics/krita/-/merge_requests/378
Now over on to the interesting stuff!
1. Parsing
This is probably going to be easiest and most of us know hows and whats about his. So, we throw any SVG with meshgradient
element, in it and Krita understands it now.
2. Making a shape out of the path.
Now, this turned out to be a bit complicated than I had anticipated, not because this was hard. But because there were a few edge cases which I overlooked. I would fix one edge case, but it would break the other one, classic whack-a-mole. So, I had to do a few rewrites of this tiny 'SvgMeshPatch and SvgMeshArray' component. Finally, I just put it all on paper and got it working for all cases, in a proper manner *.
The way I started was to manually compare some 'edge case' values to see if they're correct and fit the logic. But, there's so much that an eye can overlook. What I think I should've done is to write unit tests and then handle each edge case respectively. However the good thing is after writing unit tests, I discovered two cases where my logic wasn't right.
3. Deeper dive into the technicalities
I will try to explain how I did in a bit more detail. If you have any suggestions/critique, you're welcome.
In the big picture sense there are two things to consider, when talking about meshgradients
, meshpatch
and meshrow
(which is a linear array of meshpatches). As per the specifications, meshpatch
is a Coons Patch, which is just
the shading/fill defined by the interpolation of colors placed on the corners (i.e. edges of the curve).
Because they can be seen as a two dimensional arrays, creating an array of meshpatches, seems the most logical approach and that's what I did. However there is a slight catch, each patch shares side with other patches. Eg. in case of a gradient with a single row, up to two sides are shared. So, while parsing this had to be taken care of.
So, now we have SvgMeshArray
which is an array of of SvgMeshPatch
and each SvgMeshPatch
owns a KoPathShape
, which is how the fill boundaries are going to be defined. And that's basically all there's to it for now...
On a side note, one thing which is in my mind is that because each meshpatch is treated as an individual Shape, there's some duplication with the shared sides. But I think this is an optimization problem, which has to be taken care of, but after rendering :-)
Now a couple of obligatory screenshots, just to double check :)
PS:
* I in no way consider myself an expert, yet. Feel free to look at the code and comment on it.