mozilla / pdf.js

PDF Reader in JavaScript
https://mozilla.github.io/pdf.js/
Apache License 2.0
48.13k stars 9.94k forks source link

Interpolation is handled incorrectly. #6231

Open ruuda opened 9 years ago

ruuda commented 9 years ago

Plots produced by pgfplots can contain uncommon shaders which are not rendered correctly by pdf.js.

See for instance page 128 of the pgfplots manual (version 1.12.1). pgfplots1 On the left: pdf.js in Firefox 39.0, on the right: Adobe Reader 11.0.12. Both running on Windows 7 x64.

More examples of shaders that do not work are in the chapter about the patchplots library (page 396–412). Not all unsupported shaders render as black; some are missing: pgfplots2 pgfplots3

timvandermeij commented 9 years ago

Just to add: on page 128 the PDF uses a shading pattern with PatternType and TilingType 1.

dsprenkels commented 8 years ago

I think this issue was fixed by #6292 (after bisecting).

Snuffleupagus commented 8 years ago

I think this issue was fixed by #6292 (after bisecting).

Most of it, yes, but there're still remaining issues. See e.g. page 401, and compare with e.g. Adobe Reader.

cfeuersaenger commented 8 years ago

I have been working on shadings recently, and I stumbled over this item.

I believe there is just one item left, and it is a common issue for all involved shading types.

Some background: I wrote an (almost) exhaustive test pdf and checked my expectations within the firefox version of pdf.js shipped with ubuntu 14.04 . I also checked out the git version of pdf.js, but I haven't been able to actually use it. However, I browsed the source code and the offending issue is still in the most recent state (checked out today).

The problem appears to be that interpolation is always carried out in terms of the target color space. This violates the standard's requirements, however: if there is a function, interpolation must be carried out in the function's parameter space, and the result is to be mapped through the function.

The bug appears to be in display/pattern_helper.js and in pattern.js : pattern.js already evaluates this.context.colorFn in readComponents and pattern_helper.js merely interpoaltes the final colors (in drawTriangle, for example).

Attached is my test pdf which clearly shows the wrong interpolation.

Note that it also shows that pdf.js comes with very high quality with regards to the triangle interpolation, and it is very fast. That is an excellent job in the shader -- and I have seen a couple of other free viewers.

P.pdf

cfeuersaenger commented 8 years ago

The file P.pdf was generated from the following source that I would like to archive for future reference (but it is forbidden to upload .tex files for some reason):

\pdfcompresslevel=0 \documentclass[a4paper]{article}

\usepackage[top=1cm,left=1cm]{geometry} \usepackage{pgfplots} \pgfplotsset{compat=1.13} \usepgfplotslibrary{patchplots}

\begin{document}

\parskip=1cm \parindent=0pt

\begin{tikzpicture}
\begin{axis}[title=Rectangle from patch input (Function order 1)]
\addplot3[patch,shader=interp,patch type=rectangle] coordinates {
    (0,0,0) 
    (1,0,0) 
    (1,1,1) 
    (0,1,0) 
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[title=Rectangle from patch input (Function order 0)]
\addplot3[patch,shader=interp,colormap access=const,patch type=rectangle] coordinates {
    (0,0,0) 
    (1,0,0) 
    (1,1,1) 
    (0,1,0) 
};
\end{axis}
\end{tikzpicture}

\begin{tikzpicture}
\begin{axis}[title=Rectangle from patch input (No function)]
\addplot3[patch,shader=interp,mesh/color input=explicit,patch type=rectangle] coordinates {
    (0,0,0) [color=blue]
    (1,0,0) [color=red]
    (1,1,1) [color=green]
    (0,1,0) [color=yellow]
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[title=Rectangle from patch input (Function order 1 CMYK),
    colormap default colorspace=cmyk,
    colormap={hot}{color(0cm)=(blue); color(1cm)=(yellow); color(2cm)=(orange); color(3cm)=(red)},
]
\addplot3[patch,shader=interp,patch type=rectangle] coordinates {
    (0,0,0) 
    (1,0,0) 
    (1,1,1) 
    (0,1,0) 
};
\end{axis}
\end{tikzpicture}

\begin{tikzpicture}
\begin{axis}[title=Rectangle from patch input (Function order 1 GRAY),
    colormap default colorspace=gray,
    colormap={hot}{color(0cm)=(blue); color(1cm)=(yellow); color(2cm)=(orange); color(3cm)=(red)},
]
\addplot3[patch,shader=interp,patch type=rectangle] coordinates {
    (0,0,0) 
    (1,0,0) 
    (1,1,1) 
    (0,1,0) 
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[title=Standard use-case,colorbar,
]
\addplot3[surf,shader=interp] {x*y};
\end{axis}
\end{tikzpicture}

\clearpage

\begin{tikzpicture}
\begin{axis}[
    title=Bilinear from $4$--point patch input (Function order 1)]
\addplot3[patch,shader=interp,patch type=bilinear]
coordinates {
    (0,0,0) 
    (1,0,0) 
    (1,1,1) 
    (0,1,0) 
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[
    title=Bilinear from $4$--point patch input (Function order 0)]
\addplot3[patch,shader=interp,colormap access=const,patch type=bilinear]
coordinates {
    (0,0,0) 
    (1,0,0) 
    (1,1,1) 
    (0,1,0) 
};
\end{axis}
\end{tikzpicture}

\begin{tikzpicture}
\begin{axis}[
    title=Bilinear from $4$--point patch input (No Function)]
\addplot3[patch,shader=interp,mesh/color input=explicit,patch type=bilinear]
coordinates {
    (0,0,0) [color=blue]   
    (1,0,0) [color=red]    
    (1,1,1) [color=green]  
    (0,1,0) [color=yellow] 
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[
    colormap default colorspace=cmyk,
    colormap={hot}{color(0cm)=(blue); color(1cm)=(yellow); color(2cm)=(orange); color(3cm)=(red)},
    title=Bilinear from $4$--point patch input (Function order 1 CMYK)]
\addplot3[patch,shader=interp,patch type=bilinear]
coordinates {
    (0,0,0) 
    (1,0,0) 
    (1,1,1) 
    (0,1,0) 
};
\end{axis}
\end{tikzpicture}

\clearpage

\begin{tikzpicture}
\begin{axis}[enlargelimits,
    title=Single Triangle patch (Function order 1)]
\addplot3[patch,shader=interp,] coordinates {
    (0,0,1) 
    (1,0,0) 
    (1,1,0) 
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[enlargelimits,
    title=Single Triangle patch (Function order 0)]
\addplot3[patch,colormap access=const,shader=interp,] coordinates {
    (0,0,1) 
    (1,0,0) 
    (1,1,0) 
};
\end{axis}
\end{tikzpicture}

\begin{tikzpicture}
\begin{axis}[enlargelimits,
    title=Single Triangle patch (No Function)]
\addplot3[patch,mesh/color input=explicit,shader=interp,] coordinates {
    (0,0,1)  [color=blue]
    (1,0,0)  [color=red]
    (1,1,0)  [color=green]
};          
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[enlargelimits,
    colormap default colorspace=cmyk,
    colormap={hot}{color(0cm)=(blue); color(1cm)=(yellow); color(2cm)=(orange); color(3cm)=(red)},
    title=Single Triangle patch (Function order 1 CMYK)]
\addplot3[patch,shader=interp,] coordinates {
    (0,0,1) 
    (1,0,0) 
    (1,1,0) 
};
\end{axis}
\end{tikzpicture}

\clearpage

\begin{tikzpicture}
\begin{axis}[
    title=Quadratic Triangle (Function Order 1)]
\addplot[patch,shader=interp,patch type=triangle quadr,
    ]
coordinates {
    (0,0)  (5,4)  (0,7) 
    (2,3)  (3,6)  (-1,4) 
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[
    title=Quadratic Triangle (Function Order 2)]
\addplot[patch,shader=interp,colormap access=const,patch type=triangle quadr,
    ]
coordinates {
    (0,0)  (5,4)  (0,7) 
    (2,3)  (3,6)  (-1,4) 
};
\end{axis}
\end{tikzpicture}

\begin{tikzpicture}
\begin{axis}[
    title=Quadratic Triangle (No Function)]
\addplot[patch,shader=interp,mesh/color input=explicit,patch type=triangle quadr,
    ]
coordinates {
    (0,0) [color=red] (5,4) [color=blue] (0,7) [color=green]
    (2,3)  (3,6)  (-1,4) 
};
\end{axis}
\end{tikzpicture}

\clearpage
\begin{tikzpicture}
\begin{axis}[
    title=Single Biquadratic Quadrilateral (Function Order 1)]
\addplot[patch,shader=interp,patch type=biquadratic,
    ]
coordinates {
    (0,0)  (6,1)  (5,5)  (-1,5) 
    (3,1)  (6,3)  (2,6)  (0,3) 
    (3,3.75) 
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[
    title=Single Biquadratic Quadrilateral (Function Order 0)]
\addplot[patch,shader=interp,colormap access=const,patch type=biquadratic,
    ]
coordinates {
    (0,0)  (6,1)  (5,5)  (-1,5) 
    (3,1)  (6,3)  (2,6)  (0,3) 
    (3,3.75) 
};
\end{axis}
\end{tikzpicture}

\begin{tikzpicture}
\begin{axis}[
    title=Single Biquadratic Quadrilateral (No Function)]
\addplot[patch,shader=interp,mesh/color input=explicit,patch type=biquadratic,
    ]
coordinates {
    (0,0) [color=red]  (6,1) [color=blue]  (5,5) [color=green]  (-1,5) [color=yellow]
    (3,1)  (6,3)  (2,6)  (0,3) 
    (3,3.75) 
};
\end{axis}
\end{tikzpicture}

\clearpage
%
\begin{tikzpicture}
\begin{axis}[
    title=Single Bicubic Quadrilateral (Function order 1)]
\addplot3[patch,shader=interp,patch type=bicubic,]
coordinates {
    (0,0,1)  (1,0,0)  (2,0,0)  (3,0,0) 
    (0,1,0)  (1,1,0)  (2,1,0)  (3,1,0)
    (0,2,0)  (1,2,0)  (2,2,0)  (3,2,0)
    (0,3,0)  (1,3,0)  (2,3,0)  (3,3,0) 
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[
    title=Single Bicubic Quadrilateral (Function order 0)]
\addplot3[patch,shader=interp,colormap access=const,patch type=bicubic,]
coordinates {
    (0,0,1)  (1,0,0)  (2,0,0)  (3,0,0) 
    (0,1,0)  (1,1,0)  (2,1,0)  (3,1,0)
    (0,2,0)  (1,2,0)  (2,2,0)  (3,2,0)
    (0,3,0)  (1,3,0)  (2,3,0)  (3,3,0) 
};
\end{axis}
\end{tikzpicture}

\begin{tikzpicture}
\begin{axis}[
    title=Single Bicubic Quadrilateral (No Function)]
\addplot3[patch,shader=interp,mesh/color input=explicit,patch type=bicubic,]
coordinates {
    (0,0,1) [color=red] (1,0,0)  (2,0,0)  (3,0,0) [color=blue]
    (0,1,0)  (1,1,0)  (2,1,0)  (3,1,0)
    (0,2,0)  (1,2,0)  (2,2,0)  (3,2,0)
    (0,3,0) [color=green] (1,3,0)  (2,3,0)  (3,3,0) [color=yellow]
};
\end{axis}
\end{tikzpicture}
%
\begin{tikzpicture}
\begin{axis}[
    colormap default colorspace=cmyk,
    colormap={hot}{color(0cm)=(blue); color(1cm)=(yellow); color(2cm)=(orange); color(3cm)=(red)},
    title=Single Bicubic Quadrilateral (Function order 1 CMYK)]
\addplot3[patch,shader=interp,patch type=bicubic,]
coordinates {
    (0,0,1)  (1,0,0)  (2,0,0)  (3,0,0) 
    (0,1,0)  (1,1,0)  (2,1,0)  (3,1,0)
    (0,2,0)  (1,2,0)  (2,2,0)  (3,2,0)
    (0,3,0)  (1,3,0)  (2,3,0)  (3,3,0) 
};
\end{axis}
\end{tikzpicture}

\end{document}

timvandermeij commented 8 years ago

@cfeuersaenger Thank you providing this information!

cfeuersaenger commented 6 years ago

Note that functions of order 0 also suffer from the very same defect: interpolation is carried out in the target colorspace instead of the source color space.

Attaching a simple example to demonstrate the effect.

order0.pdf

I reproduced the failure with todays "Demo" pdf.js (both with order0.pdf and with P.pdf uploaded 1.5 years ago).


I copy my early problem assessment from above as it is somewhat buried in my lengthy comments:

The bug appears to be in display/pattern_helper.js and in pattern.js : pattern.js already evaluates this.context.colorFn in readComponents and pattern_helper.js merely interpolates the final colors (in drawTriangle, for example).