[Tex/LaTex] How to make correct shading with hsb color model

colortikz-pgf

I want to use hsb color model to shading.

First rectangle shows a shading using rgb color model.

Second rectangle shows a shading using hsb color model. But I don't understand why I don't obtain the same shading that with my third rectangle.

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  % create some hsb colors
  \colorlet{redhsb}[hsb]{red}%
  \colorlet{bluehsb}[hsb]{blue}%
  \colorlet{greenhsb}[hsb]{green}%

  % first shading with rgb color model
  \fill[left color=red,right color=blue] (0,1) rectangle (2,0);

  % second shading with hsb color model
  \fill[left color=redhsb,right color=bluehsb] (0,0) rectangle (2,-1);

  % third shading with hsb color model
  \fill[left color=redhsb,right color=greenhsb] (0,-1) rectangle (1,-2);
  \fill[left color=greenhsb,right color=bluehsb] (1,-1) rectangle (2,-2);
\end{tikzpicture}
\end{document}

enter image description here

Edit: In fact, TikZ calculates the middle color (using color model of the left color). Then, it converts all three colors to rgb model and make shading…

I add a fourth rectangle:

% fourth shading with rgb color model
\fill[left color=red,right color=green] (0,-2) rectangle ++(1,-1);
\fill[left color=green,right color=blue] (1,-2) rectangle ++(1,-1);

enter image description here

So, my question is: Can TikZ do real hsb (HSV) shading?

Best Answer

The following happens when you do \fill[left color=redhsb,right color=bluehsb] (0,0) rectangle (2,-1);

  • TikZ sets three internal colors:

    • tikz@axis@bottom=bluehsb
    • tikz@axis@middle=redhsb!50!bluehsb
    • tikz@axis@top=redhsb

    The middle color is calculated by xcolor, which does the mixing in the first color model (HSB in this case).

  • TikZ uses a shading declared by

    \pgfdeclareverticalshading[tikz@axis@top,tikz@axis@middle,tikz@axis@bottom]{axis}{100bp}{%
      color(0bp)=(tikz@axis@bottom);
      color(25bp)=(tikz@axis@bottom);
      color(50bp)=(tikz@axis@middle);
      color(75bp)=(tikz@axis@top);
      color(100bp)=(tikz@axis@top)}
    
  • pgf basically passes this through to \pgfsys@vertshading.

  • The definition of this command depends on the output driver. For PDF it is

    \def\pgfsys@vertshading#1#2#3{%
      {%
        \pgf@parsefunc{#3}%
        \pgfmathparse{#2}%
        \setbox\pgfutil@tempboxa=\hbox to\pgfmathresult pt{\vbox to\pgf@max{\vfil\pgfsys@invoke{/Sh sh}}\hfil}%
        \pgf@process{\pgfpoint{#2}{\pgf@max}}%
        \pdfxform resources {%
          /Shading << /Sh << /ShadingType 2
          /ColorSpace /DeviceRGB
          /Domain [\pgf@pdfparseddomain]
          /Coords [0 \pgf@doma\space0 \pgf@domb]
          /Function \pgf@pdfparsedfunction
          /Extend [false false] >> >>}\pgfutil@tempboxa% <<
        \expandafter\xdef\csname @pgfshading#1!\endcsname{\leavevmode\noexpand\pdfrefxform\the\pdflastxform}%
      }%
    }
    

    I won't pretend to understand everything that is going on, but as far as I can tell, pgf only writes the 3 computed colors and tells the PDF-viewer to do the shading by linear interpolation. In other words, TikZ doesn't seem to do the shading calculations itself.

As you see above, the color space is set to DeviceRGB. Indeed I think that PDF doesn't support HSL (though I haven't checked to PDF documentation).

As far as I understand, the only way to get HSL interpolation is to write a PostScript shader that does the interpolation and then converts the result to RGB.

Related Question