Jump to content

GSoC/2020/StatusReports/SharafZaman: Difference between revisions

From KDE Community Wiki
Created page with "==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. The..."
 
 
(14 intermediate revisions by the same user not shown)
Line 3: Line 3:
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.
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===
== Project Goals ==


* Parse Mesh Gradients  
* Parse Mesh Gradients  
Line 14: Line 14:
*** Status: '''DONE'''
*** Status: '''DONE'''
** Render meshes with Bicubic Interpolation
** Render meshes with Bicubic Interpolation
*** Status: '''Partially Done'''
*** Status: '''DONE'''


* Saving files with Mesh Gradients
* Saving files with Mesh Gradients
** Status: '''Doing'''
** Status: '''DONE'''
 
* Tests
** Parsing: '''DONE'''
** Rendering: '''DONE'''
** Writer classes: '''DONE'''


* Tooling
* Tooling
** Status: '''PENDING'''
** Status: '''DONE'''
 
* User Manual
** Status: '''Pending'''
 
== Links ==
* [https://invent.kde.org/graphics/krita/-/merge_requests/378 Merge Request]
* [https://invent.kde.org/graphics/krita/-/tree/sh-zam/T13101-svg-mesh-gradients Branch]
* [https://phabricator.kde.org/T13101 Phabricator Task]
 
== Work Report ==
 
=== Parsing ===
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. So, I replaced it with a 2D array during the later stage.
 
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].
 
===== Relevant Commits =====
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=3c8586f64d4e3b20fd7241ebc134a138a0c55db6 Add parser for parsing meshgradient SVG element]
 
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=4c04fc0b57b12977d9f5b089e6fa645bd1dc706a Create KoPathShape from meshpatch stops]
 
=== 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.
 
The final rendering of bicubic 2x2 meshgradient:
[[File:Bicubic-meshgradient.png|Bicubic rendering, no machbanding]]
 
I have gone into the details in my [https://www.sh-zam.com/2019/07/blog-post.html  second Blog post]
 
===== Relevant Commits =====
 
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=a60008e4251822801156ca91e91345d769bd60c0 Send SvgMeshGradient from SvgParser to KoMeshGradientBackground]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=3596ab30d971673f95b969cdb33aca98074e72d8 Implement meshpatch subdivision]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=16ad0dc34cc89be93a0c0a0a3c5dc9ffa5527969  Implement divide and conquer algorithm to render meshpatches]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=9ed4694cb8ccb5239c3476b4b87ab16a431be389  Check if color variation is less than the threshold ]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=80145ffc212bc9e9d3adda4bad1d1ae207817da5 Handle transforms and gradientUnits for meshgradients]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=7d06c39166d9298ec5bdd84f8a0f0442b1af8c8d Render meshgradient on QImage and then on QPainter]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=c0f66cd6298e0b71a0cda09c6ad76ab3209ba8eb  Implement bicubic interpolation for meshgradients ] (buggy)
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=e689655164f0c33ce8e8e8485fc1de836f8fd37f Fix the calculus of bicubic interpolation]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=86559bf2433e821905d1b7516b93f556dc3030b1 Implement bicubic interpolation for meshgradients] (working)
 
=== Saving ===
This was straight forward and took me a little time to get everything working.
 
Example of an SVG with <code>meshgradient</code> created and exported using Krita:
<pre><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></pre>
 
===== Relevant Commits =====
[https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=69141fe320238707e0a5268661ce816b1830e38f  Implement saving for meshgradients ]
 
=== 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.
 
===== Relevant Commits =====
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=1a80fd25887a9157d68c5470581157c0506ed59a  Render based tests for meshgradients ]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=4abd2de76f81529b316fa6ba628f4051bbb6bfba  Test: Minor fixes ]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=2da26c7fdd0fc553ec6f18486f7eb79ca9cbecc2  Fix transparent meshgradients ]
 
=== 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]]
 
===== Relevant Commits =====
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=2d04d6863fac301417f1672255928a914e888a8a  Prelude for meshgradients GUI tool ]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=40617ebfac0f0b9edd6c1ba9c93f2e17bcebb18e  Add ability to create default meshgradient ]
* [https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=a77bbc9a4e3ecdfbb6a6f38cf82c464b5c833d54  Implement tooling for meshgradients ]
 
=== Last commit for the GSoC Project ===
https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=a77bbc9a4e3ecdfbb6a6f38cf82c464b5c833d54
<pre>
commit a77bbc9a4e3ecdfbb6a6f38cf82c464b5c833d54 (origin/sh-zam/T13101-svg-mesh-gradients)
Author: Sharaf Zaman <[email protected]>
Date:  Mon Aug 24 13:21:27 2020 +0000
 
    Implement tooling for meshgradients
</pre>
 
 
=== What is remaining? ===
* User documentation
* Optimization
** Color comparison using LCMS is expensive
** rendering only the changed patches (to be discussed)
* UI/UX polish
** Highlighting selected nodes
** Highlighting selected curves
 
== Blog ==
Blog Posts:
* [https://www.sh-zam.com/2019/08/status-update-hello-it-has-been-time.html  Hello once again! ]
* [https://www.sh-zam.com/2019/07/blog-post.html  You can open Mesh Gradients in Krita now!]

Latest revision as of 09:29, 2 September 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: DONE
    • Writer classes: DONE
  • Tooling
    • Status: DONE
  • User Manual
    • Status: Pending

Links

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. So, I replaced it with a 2D array during the later stage.

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

Relevant Commits

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

Relevant Commits

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>
Relevant Commits

Implement saving for meshgradients

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.

Relevant Commits

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

Relevant Commits

Last commit for the GSoC Project

https://invent.kde.org/graphics/krita/-/merge_requests/378/diffs?commit_id=a77bbc9a4e3ecdfbb6a6f38cf82c464b5c833d54

commit a77bbc9a4e3ecdfbb6a6f38cf82c464b5c833d54 (origin/sh-zam/T13101-svg-mesh-gradients)
Author: Sharaf Zaman <[email protected]>
Date:   Mon Aug 24 13:21:27 2020 +0000

    Implement tooling for meshgradients


What is remaining?

  • User documentation
  • Optimization
    • Color comparison using LCMS is expensive
    • rendering only the changed patches (to be discussed)
  • UI/UX polish
    • Highlighting selected nodes
    • Highlighting selected curves

Blog

Blog Posts: