Jump to content

GSoC/2020/StatusReports/SharafZaman: Difference between revisions

From KDE Community Wiki
Line 27: Line 27:
** Status: '''PENDING'''
** Status: '''PENDING'''


== Work Report ==  
== Work Report ==
 
=== Parsing ===
=== Parsing ===
Objective here was to parse raw SVG files containing <code>meshgradient</code> elements into meaningful path and color data. I have gone into the depths of how I implemented in my [https://www.sh-zam.com/2019/08/status-update-hello-it-has-been-time.html first blog post].
Objective here was to parse raw SVG files containing <code>meshgradient</code> elements into meaningful path and color data. So, the raw path and color data would be transformed into an array of <code>SvgMeshPatch</code>es. Initially the data structure for storing path data was <code>KoPathShape</code>. But later I found it was too heavy for the memory.
 
I have gone into the depths of how I implemented in my [https://www.sh-zam.com/2019/08/status-update-hello-it-has-been-time.html first blog post].
 
=== Rendering ===
This was the primary objective. We use the vertex data to produce the final render. There are two types of shading which could be supported by <code>meshgradients</code>.
 
    1. Bilinear
    2. Bicubic
 
Bilinear was trivial and only took me a few days to implement after some help from my mentor. And the results were accurate - accurate enough, but not to the pixel.
 
So, the final rendering of bilinear 2x2 meshgradient:
 
[[File:Bilinear-meshgradient.png|Left: Krita, Right: Inkscape]]
 
Because, unfortunately the bilinear shading suffers from the machbanding effect. The standard (draft), mentions implementing another shading Bicubic to counter act this.
 
But bicubic was hard, because it was easy to get wrong. The rendered image I kept getting was either something utterly nonsensical or something that is similar to Bilinear (which too is very wrong). The solution here, after a week of reading a lot of code and fixing some mathematical mistakes was to find the derivatives of the corners and use them for the subdivided patches.


=== Rendering ===
The final rendering of bicubic 2x2 meshgradient:
Use the vertex data and interpolate it Bilinearly and Bicubically to produce the final render. I have gone into the details in my [https://www.sh-zam.com/2019/07/blog-post.html  second Blog post]
[[File:Bicubic-meshgradient.png|Bicubic rendering, no machbanding]]


Here is the final render of both Bilinear and Bicubicaly blended meshgradients in Krita.
I have gone into the details in my [https://www.sh-zam.com/2019/07/blog-post.html  second Blog post]
<ul>
<li style="display:inline-block;">[[File:Bilinear-specs.png|thumb|Bilinearly blended meshgradient]]</li>
<li style="display:inline-block;">[[File:Bicubic-specs.png|thumb|Bicubicaly blended meshgradient]]</li>
</ul>


=== Saving ===
=== Saving ===
Even though you cannot change the meshgradient (fill) itself. But what you can change is the parent shape, add properties like rotation, transformation, cut and copy the shape (which will allow rendering meshgradient again). This all is facilitated by implementing the saving feature.
This was straight forward and took me a little time to get everything working.


Example of an SVG with <code>meshgradient</code> exported using Krita:
Example of an SVG with <code>meshgradient</code> created and exported using Krita:
<pre><code>
<pre><code>
<?xml version="1.0" standalone="no"?>
<<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Krita: https://krita.org -->
<!-- Created using Krita: https://krita.org -->
<svg xmlns="http://www.w3.org/2000/svg"  
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     xmlns:krita="http://krita.org/namespaces/svg/krita"
     xmlns:krita="http://krita.org/namespaces/svg/krita"
     xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
     xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
     width="595.44pt"
     width="1872pt"
     height="842.4pt"
     height="1152pt"
     viewBox="0 0 595.44 842.4">
     viewBox="0 0 1872 1152">
<defs>
<defs>
   <meshgradient id="meshgradient0" gradientUnits="userSpaceOnUse" x="81.952896" y="97.695755" type="bicubic">
   <meshgradient id="meshgradient0" gradientUnits="objectBoundingBox" x="0.0601435354570587" y="0.077639733323807" type="bilinear">
   <meshrow id="meshrow0">
   <meshrow id="meshrow0">
     <meshpatch id="meshpatch0">
     <meshpatch id="meshpatch0">
     <stop path="C 106.953,72.6958 156.953,122.696 181.953,97.6958" stop-color="#000000" stop-opacity="1"/>
     <stop path="C 0.3934768688,0.07763973332 0.4539196719,0.242679652 0.7872530052,0.242679652" stop-color="#ffffff" stop-opacity="1"/>
     <stop path="C 206.953,122.696 156.953,172.696 181.953,197.696" stop-color="#000000" stop-opacity="1"/>
     <stop path="C 0.7872530052,0.5760129854 0.8653499483,0.4736763632 0.8653499483,0.8070096966" stop-color="#5b0000" stop-opacity="1"/>
     <stop path="C 156.953,222.696 106.953,172.696 81.9529,197.696" stop-color="#ff0000" stop-opacity="1"/>
     <stop path="C 0.532016615,0.8070096966 0.5469777569,0.8216503345 0.2136444235,0.8216503345" stop-color="#ffffff" stop-opacity="1"/>
     <stop path="C 56.9529,172.696 106.953,122.696 81.9529,97.6958" stop-color="#000000" stop-opacity="1"/>
     <stop path="C 0.2136444235,0.4883170012 0.06014353546,0.4109730667 0.06014353546,0.07763973332" stop-color="#5b0000" stop-opacity="1"/>
     </meshpatch>
     </meshpatch>
   </meshrow>
   </meshrow>
   </meshgradient>
   </meshgradient>
  </defs>
  </defs>
<g id="layer1" transform="matrix(2.83465058 0 0 2.83465058 0 38.3781321184511)" fill="none">
<rect id="shape0" transform="matrix(0.838249023313196 -0.545287607519627 0.545287607519627 0.838249023313196 371.360217722377 348.486677321207)" fill="url(#meshgradient0)" fill-rule="evenodd" stroke="#000000" stroke-opacity="0" stroke-width="0" stroke-linecap="square" stroke-linejoin="bevel" width="401.04" height="811.44"/>
  <rect id="rect1619" transform="translate(-51.952896, -67.695755)" fill="url(#meshgradient0)" stroke="#007b00" stroke-width="0.264999" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="2" width="651.95288" height="667.69574"/>
</g>
</svg>
</svg>
</code></pre>
</code></pre>
=== Testing ===
Krita has a great testing suite, which takes the raw SVG, renders it, compares it, saves it, then renders it again and then compares it again. So, adding a 12 new tests to this was a trivial task. But unfortunately, because Inkscape was used as the source of truth, some pixels were incorrectly renderer.
The reason was <code>QPainter</code> drawing an extra pixel layer to the right and the bottom side. So,in some cases where I'd expect it to put one pixel on screen it would be two, which created artifacts, especially when the patch size was small. The partial solution to this after getting intuition from my mentors was to use <code>drawPoint(...)</code> method.
=== Tooling ===
This was the final part to make the meshgradients feature in Krita complete. For this I created a new option in the Tool options docker, through which we can add rows, columns, change the color of stops and change the shading type.
[[File:Tooloptions-meshgradient.png|Tool Options for meshgradient]]
Then the next part was handling the movement of the bezier curves that create the mesh, for this I implemented a new class <code>KoShapeMeshGradientHandles</code>, which handles handle movements and passes them to <code>SvgMeshArray</code> and <code>SvgMeshPatch</code> to update the shape where it has a little branch to explicitly handle the shared sides.
[[File:Handles-meshgradient.png|Manipulating the SVG containing meshgradients]]


== Links ==
== Links ==

Revision as of 08:54, 26 August 2020

SVG Mesh Gradients in Krita

Mesh Gradients are important to produce real looking vector graphics. They are also very helpful and straightforward to use for an artist. They are supported by Inkscape, Adobe Illustrator, Cairo, Scribus and many other graphics programming tool kits. My project is to add support for mesh gradients in Krita. By the end of GSoC Krita, should be able to open, edit, create vector objects using mesh gradients.

Project Goals

  • Parse Mesh Gradients
    • Status: DONE
  • Handle Attributes and transforms
    • Status: DONE
  • Render Mesh Gradients
    • Render meshes with Bilinear Interpolation
      • Status: DONE
    • Render meshes with Bicubic Interpolation
      • Status: DONE
  • Saving files with Mesh Gradients
    • Status: DONE
  • Tests
    • Parsing: DONE
    • Rendering: Doing
    • Writer classes: Doing
  • Tooling
    • Status: PENDING

Work Report

Parsing

Objective here was to parse raw SVG files containing meshgradient elements into meaningful path and color data. So, the raw path and color data would be transformed into an array of SvgMeshPatches. Initially the data structure for storing path data was KoPathShape. But later I found it was too heavy for the memory.

I have gone into the depths of how I implemented in my first blog post.

Rendering

This was the primary objective. We use the vertex data to produce the final render. There are two types of shading which could be supported by meshgradients.

   1. Bilinear
   2. Bicubic

Bilinear was trivial and only took me a few days to implement after some help from my mentor. And the results were accurate - accurate enough, but not to the pixel.

So, the final rendering of bilinear 2x2 meshgradient:

Left: Krita, Right: Inkscape

Because, unfortunately the bilinear shading suffers from the machbanding effect. The standard (draft), mentions implementing another shading Bicubic to counter act this.

But bicubic was hard, because it was easy to get wrong. The rendered image I kept getting was either something utterly nonsensical or something that is similar to Bilinear (which too is very wrong). The solution here, after a week of reading a lot of code and fixing some mathematical mistakes was to find the derivatives of the corners and use them for the subdivided patches.

The final rendering of bicubic 2x2 meshgradient: Bicubic rendering, no machbanding

I have gone into the details in my second Blog post

Saving

This was straight forward and took me a little time to get everything working.

Example of an SVG with meshgradient created and exported using Krita:

<code>
<<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Krita: https://krita.org -->
<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:krita="http://krita.org/namespaces/svg/krita"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    width="1872pt"
    height="1152pt"
    viewBox="0 0 1872 1152">
<defs>
  <meshgradient id="meshgradient0" gradientUnits="objectBoundingBox" x="0.0601435354570587" y="0.077639733323807" type="bilinear">
   <meshrow id="meshrow0">
    <meshpatch id="meshpatch0">
     <stop path="C 0.3934768688,0.07763973332 0.4539196719,0.242679652 0.7872530052,0.242679652" stop-color="#ffffff" stop-opacity="1"/>
     <stop path="C 0.7872530052,0.5760129854 0.8653499483,0.4736763632 0.8653499483,0.8070096966" stop-color="#5b0000" stop-opacity="1"/>
     <stop path="C 0.532016615,0.8070096966 0.5469777569,0.8216503345 0.2136444235,0.8216503345" stop-color="#ffffff" stop-opacity="1"/>
     <stop path="C 0.2136444235,0.4883170012 0.06014353546,0.4109730667 0.06014353546,0.07763973332" stop-color="#5b0000" stop-opacity="1"/>
    </meshpatch>
   </meshrow>
  </meshgradient>
 </defs>
<rect id="shape0" transform="matrix(0.838249023313196 -0.545287607519627 0.545287607519627 0.838249023313196 371.360217722377 348.486677321207)" fill="url(#meshgradient0)" fill-rule="evenodd" stroke="#000000" stroke-opacity="0" stroke-width="0" stroke-linecap="square" stroke-linejoin="bevel" width="401.04" height="811.44"/>
</svg>

</code>

Testing

Krita has a great testing suite, which takes the raw SVG, renders it, compares it, saves it, then renders it again and then compares it again. So, adding a 12 new tests to this was a trivial task. But unfortunately, because Inkscape was used as the source of truth, some pixels were incorrectly renderer.

The reason was QPainter drawing an extra pixel layer to the right and the bottom side. So,in some cases where I'd expect it to put one pixel on screen it would be two, which created artifacts, especially when the patch size was small. The partial solution to this after getting intuition from my mentors was to use drawPoint(...) method.

Tooling

This was the final part to make the meshgradients feature in Krita complete. For this I created a new option in the Tool options docker, through which we can add rows, columns, change the color of stops and change the shading type.

Tool Options for meshgradient

Then the next part was handling the movement of the bezier curves that create the mesh, for this I implemented a new class KoShapeMeshGradientHandles, which handles handle movements and passes them to SvgMeshArray and SvgMeshPatch to update the shape where it has a little branch to explicitly handle the shared sides.

Manipulating the SVG containing meshgradients

Links

Blog Posts: