GSoC/2019/StatusReports/Albertoefg
Krita: Animated Vector Brush
Summary
- Project Name: Animated Vector Brush for Krita
- Proposal: Google Docs Proposal
- Related blog: albertoefg.info
- Abstract: This projects aims to implement an Animated Vector Brush for Krita, which uses SVG as a source file for the brush tips. The purpose of an animated brush is to change the brush tips automatically with every dab on the canvas. This allows for a quick way to paint different images, create random and fun designs, and explore new ways to paint that are not easy in physical world. In the other hand, using SVG as a source makes it easy to share the brush tips as they are saved as plain text, as opposed to raster image brushes.
Project Goals
- Write documentation for end users as per the Krita Contribution Guide.
- Define the structure of the SVG file containing the source tips for the Animated Vector Brush
- Load AVB
- Parse SVG
- Render brush tips as images
- Write a parasite class to hold the index
- Write a VectorAnimatedBrush class
- Write a VectorShapeObject class
- Write a helper class to populate the VectorAnimatedBrush class
- Change brush tip with every dab
- Phabricator https://phabricator.kde.org/T10930
- Personal Blog https://albertoefg.info/blog/kde.html
Implementations Status
Write documentation
- Goal: The documentation should be written for end users as per the Krita Contribution Guide, it should also be sufficient for the users to understand and use the brush.
- Status: The documentation is mostly done, it is only necessary to add the proper screenshots and update in case of changes.
- TODO: Add screenshots.
- Related blog post: My first two weeks on Google Summer of Code
Commits and Differentials
Load AVB
- Goal: The Animated Vector Brush file should be loaded and call the proper class, in this case
KisVectorAnimatedBrush
. - Status: The implementation is done, the loading of the AVB by the user is using the
+import
tab. - Related blog post: My first two weeks on Google Summer of Code
Commits and Differentials
Parse SVG
- Goal: The SVG should be parsed and transformed to
KoShape
as necessary, so Krita can use them to setbrushTipImage
, paint dabs or change the size of the brush tip. - Status: Done.
- Related blog post: Basic functionality almost ready
Commits and Differentials
Render brush tips as images
- Goal: The
KoShapes
should be transformed toQImage
as necessary, so Krita can use them to setbrushTipImage
, paint dabs or change the size of the brush tip. - Status: Done.
- Related blog post: Basic functionality almost ready
Commits and Differentials
Write a parasite class to hold the index
- Goal: Write a parasite class that will aid
KisVectorAnimatedBrush
class, holding the values of the Index and related data. - Status: In Progress. Although the majority of the class works now, there is a need for comments and cleaning. It can also be extended to add more functionality like different types of sequences: tilt, velocity, etc.
- Related blog post: Implementing a derivated class of kis_brushes_pipe
Commits and Differentials
Write a KisVectorAnimatedBrush class
- Goal: Write a the class
KisVectorAnimatedBrush
that will inherit fromKisBrushesPipe
to work as a brush pipe ofVectorShapeObject
brushes, it will also have aPrivate
struct to hold aKisVectorHelperPipe
object. - Status: In Progress. The class is mostly done, however it can be extended to get more functionality.
- Related blog post:
Commits and Differentials
Write a VectorShapeObject class
- Goal: Write a class that will work as a single brush that will hold a single
KoShape
and a singleQImage
. A series of objects of this class will populate theVectorAnimatedBrush
class, using theKisVectorHelperPipe
class. - Status: Done.
- Related blog post: Krita Sprint News
Commits and Differentials
- Phabricator T10930
- eae68aa7: Add KisVectorHelperPipe Class Note: This class was added in the same commit as the helper pipe class.
- 0b2ef645: Inherit from KisScalingsizebrush in KisVectorShapeObject
- ebdd8f8c: Convert brushTip to MaskImage
- 9e6ee547: Add appropriate brushType
- 058756d2: Intitalize Private struct to hold KisVectorHelperPipe object
Write a Helper Pipe class to populate the KisVectorAnimatedBrush
- Goal: Write a helper class for
KisVectorAnimatedBrush
that will aid holding a parasite object of the class written earlier, and populate the pipe brush withVectorShapeObject
brushes. - Status: Done.
- Related blog post: Krita Sprint News
Commits and Differentials
Change brush tip with every dab
- Goal: Change the
brushTipimage
with every dab - Status: Done.
- Related blog post: Building a Vector Animated Brush
Commits and Differentials
All Commits
- bd654077: Added vectorAnimatedBrush to brush server
- ddee5b98: Load Vector Animated Brush
- d1afdb40: Return false for empty SVG
- 7ed4b9c1: Use SvgParser to loadFromDevice
- cffe647e: Set brushTipImage from the KoShapePainter
- b024e4fb: Render QImages only once
- 065ac9ba: Added a parasite helper class
- 4d3fbd9b: Fix the segmentation fault
- 058756d2: Intitalize Private struct to hold KisVectorHelperPipe object
- 75365412: Use paintDevice to render brush
- bbd5667e: Add destructor for Private object
- 0b2ef645: Inherit from KisScalingsizebrush in KisVectorShapeObject
- ebdd8f8c: Convert brushTip to MaskImage
- 9e6ee547: Add appropriate brushType
- eae68aa7: Add KisVectorHelperPipe Class
- 30c2f3c9: Populate brushesPipe with VectorShapeObjects
- 2ed1c1d5: Fix iteration of shapesToimages
- cda1fd56: Use chooseNextBrush to update the Index
- e8248790: Use KisVectorHelperPipe for the Index
Chronicle
Community Bonding Period
During this period of the Google Summer of Code I tried to contribute to Krita in different ways, including helping users on IRC and Reddit, and also improving the Krita documentation.
The biggest amount of work I did was to contribute with the Selections Tools section of the manual, as suggested by Wolthera van Hövell. This helped me to understand better how to write documentation, how to use git and gitlab, and how to work with an Open Source project. My work was merged right before the Google Summer of Code started. Link to the closed merge request
First two weeks
During this period I wrote the documentation for my brush. I don't have experience with writing real world software, but I've read it is always a good idea to start writing the end user documentation, this way there is a clear path on what to achieve at the end of the project.
I also read the specification on Scalable Vector Graphics (SVG) 1.1, because I wanted to know as much as I could about the <symbol>
and <use>
elements. Upon reading this with more detail, I realized it would be a bad idea to use <symbol>
for this type of brushes.
This is because ‘symbol’ elements are never rendered directly; their only usage is as something that can be referenced using the ‘use’ element.
Using a <symbol>
would imply that the users have a broken workflow when it comes to editing the brush tips, because they won't be able to edit the brush in the Krita canvas, as the canvas should not show <symbol>
elements only <use>
elements. And changing this behavior would be against the SVG specification.
Because of this, I asked for permission to change the original design of the brush, to use the <g>
element instead. This would bring a number of benefits: 1) the brush tips could be open, edited and saved in the Krita canvas; 2) the brush tips could be parsed using the SvgParser
from the Flake library. 3) It would simplify the user workflow.
Second and Third weeks
During this period I worked on implementing the base class for my brush and to have the basic functionality, this includes: Opening an AVB with the +import
tab, parse the XML from the SVG to be a QList
of KoShapes
, adding the mimetype of .avb
to Krita; during this period I didn't have much trouble with Object Oriented Programming as the majority of the work was using functions and implementing the simple things, not touching classes much.
Fourth and Fifth weeks
This was a harder time, I started to work with classes and to implement the index that will change with every dab, changing the images accordingly. This was the first time I really had to understand the way OOP, and C++ works, it wasn't an easy time for me. I tried to implement a class that inherited from KisBrushesPipe
, however I was not successful, but I had some small achievements, I started to understand how a brush pipe should work on Krita, what basic functionality I needed and how to achieve this. During this period my mentor started to work with me closer, to make sure I understood everything and were not completely lost.
Sixth and Seventh weeks
This was a period of darkness, although my mentor was really helpful and patient with me. I started to read more of the Krita codebase, specially to look at how KisBrushesPipe and KisImagePipeBrush work, it was really hard, a lot of the functionality was not obvious to me and there was no easy way to understand it.
My mentor guided me by telling me to work on a single class and not look at those classes for the moment, that way I could start to improve my brush in a simple way, this was a good advice. I stopped trying to implement those things and slowly worked on built a more humble thing. Here is a point where I did my biggest mistake, I didn't write tests. I was feeling really confused and had a lot of new things to understand and work with, and the unit tests only added to the complexity, so I tried to focus on learning more about the work I was doing before adding another layer of complexity to my code.
Eight and Ninth weeks
Finally out of the darkness. During this period thanks to the help of my mentor I started to work slowly to improve my brush, to understand more and more of the code of Krita and to implement more functionality.
This weeks were also the ones when the Krita Sprint happened, so I got to talk to my mentor directly, we did a lot of progress, when the Krita Sprint ended my project had a lot of new classes and functionality. I was able to read the code of KisImagepipebrush
and have a much clear idea of what was happening and why.
Unfortunately this wasn't all a victory, I did a huge amount of progress and this two weeks were probably the ones were I learned the most about programming, but I also didn't produce as good code as I wish I could, there were was a lot of code I needed to clean, I still needed to write unit testing and have lots of things to improve. But overall I am quite proud of how much I learned and improved.
Conclusion
Overall the whole I am proud of all the learning and work I did. It has been one of the greatest experiences to learn and collaborate with such an amazing project as Krita, to meet all the team during the Krita Sprint and to realize how much more I can do to help.
I still have a lot to improve my project. Fortunately the end of the Google Summer of Code does not mean the end of my collaboration with Krita, which I hope will last for years to come and I feel proud to be a member of this community.
I have to admit my project is not yet ready to be part of Krita. There are still things to do, it is necessary to clean the code, to add more comments, and to add more tests. But thanks to the progress of the last few weeks I am confident it won't be that long before my brush can become part of Krita.
The future
Talking to my mentor and some other Krita developers, I was suggested to read more programming books, one of them being "Design Patterns: Elements of Reusable Object-Oriented Software". I plan to read this book to learn more and be able to produce better code for Krita.
I was also suggested by Tiar to start writing and small program that would reset the configuration of Krita, to help non-technical users, as suggested by this phabricator task.
Of course I want to keep improving my brush to take it to the next level, adding new features and options that were beyond the scope of the Google Summer of Code period and my coding abilities.