27

In my opinion, external pictures are often more pleasing to the eye on a white background when they have fuzzy rather than "sharp" borders. For instance, compare this

enter image description here

to that

enter image description here

Adding fuzzy borders is straighforward in image-editing tools such as GIMP (Filters >> Decor >> Fuzzy borders...).

However, using 3rd-party tools for adding fuzzy borders on an image to be included in a .tex file lacks maintainability. What if I suddenly decide that the fuzzy borders I added to my original image are too narrow or too wide, or that I no longer want fuzzy borders? I would have to modify all my image files in a 3rd-party image-editing tool...

Is there a way to add fuzzy borders to external pictures programmatically in LaTeX, without modifying the original image file? That would be nice! You could then tweak some fuzzy-border settings globally, decide whether or not you want fuzzy borders at all, etc. directly inside (rather than outside) your input file.

My attempt: I didn't find any way of using \shade with different opacity for top color and bottom color. Therefore, I thought I could draw concentric rectangular paths of decreasing dimensions and decreasing opacity on top of the picture, but that seems like a very wasteful way of doing things.

I'm not asking for a full solution here, merely for pointers.

Can you think of a better approach?

Related questions on TeX.SE include:

but I haven't been able to adapt the solutions proposed there.

34
+100

You could use TikZ together with an auxiliary pdf file that contains a square that is filled with a white with full opacity to white with no opacity shade. Then overlay this auxiliary pdf on all four sides, stretching them to the correct size. (UPDATE: See update below for how to do this without the external pdf file).

Using an external pdf file

The code below produces the following output:

enter image description here

\documentclass{article}

\usepackage{tikz}
\usepackage{graphicx}

\begin{document}

\makeatletter

\newlength\pic@height
\newlength\pic@width
\newlength\shade@width
\shade@width=32pt

\begin{tikzpicture}

\node (pic) { \includegraphics{pic.jpg} };

\pgf@process{\pgfpointdiff{\pgfpointanchor{pic}{south east}}{\pgfpointanchor{pic}{north west}}}

\setlength\pic@height{\pgf@y}
\setlength\pic@width{\pgf@x}

\node at (pic.east) [anchor=west, xshift=1pt, rotate=180] 
    { \includegraphics[width=\shade@width, height=\pic@height]{border.pdf}};
\node at (pic.west) [anchor=west, xshift=-1pt] 
    { \includegraphics[width=\shade@width, height=\pic@height]{border.pdf} };
\node at (pic.north) [anchor=west, xshift=-.5\pic@width, yshift=1pt, rotate=-90] 
    { \includegraphics[width=\shade@width,height=\pic@width]{border.pdf} };
\node at (pic.south) [anchor=west, xshift=.5\pic@width, yshift=-1pt, rotate=90] 
    { \includegraphics[width=\shade@width,height=\pic@width]{border.pdf} };

\end{tikzpicture}

\end{document}

This code requires an auxiliary file border.pdf as I described above. To make it look more fuzzy you would have to play with this pdf file.

For better results, it may be nicer to have a separate auxiliary pdf for the corners, although I'm not sure if that will be rendered properly on every pdf viewer, because you would have to place the auxiliary pdfs right next to each other.

Using pgf shading (does NOT work in XeLaTeX! Shading is not supported by XeLaTeX.)

The drawback of the above approach is that you need an external pdf file. On top of that, the corners of the output above look a bit strange if you look closely. This latter has to do with the "derivative" of the opacity parameter in the horizontal direction in my auxiliary pdf file (it is piecewise linear: constant at 1, linearly decrease, constant at 0). Perhaps it is better to let the opacity be a smooth function of the x coordinate (for example the logistic function exp(x)/(1+exp(x))).

The following package smoothpic.sty uses pgf shading, which uses no external pdf file and has smoother shading.

\NeedsTeXFormat{LaTeX2e}[1995/12/01]
\ProvidesPackage{smoothpic}[2013/05/05 Smoothpic]

% This package defines the \smoothpic command which is a wrapper around the 
% \includegraphics command. Any optional arguments given to \smoothpic are 
% passed to \includegraphics.
% Example: \smoothpic[width=5cm]{image.jpg}
%
% To change the width of the shade, change the value of \smoothpicshadewidth.
% Example: \smoothpicshadewidth=32pt
%

\RequirePackage{tikz}
\RequirePackage{graphicx}
\usetikzlibrary{calc}

% ---- Desired width of the shade
\newlength\smoothpicshadewidth
\smoothpicshadewidth=24pt

\newlength\smoothpic@height%
\newlength\smoothpic@width%

% ---- Define horizontal shading, with opacity according 
%      to a logistic function
\pgfdeclarehorizontalshading{smoothpichshading}{100bp}
  { color(0pt)=(transparent!0);
    color(25bp)=(transparent!0);
    color(28bp)=(transparent!1);
    color(31bp)=(transparent!2);
    color(34bp)=(transparent!3);
    color(38bp)=(transparent!6);
    color(41bp)=(transparent!11);
    color(44bp)=(transparent!20);
    color(47bp)=(transparent!33);
    color(50bp)=(transparent!50);
    color(53bp)=(transparent!67);
    color(56bp)=(transparent!80);
    color(59bp)=(transparent!89);
    color(63bp)=(transparent!94);
    color(66bp)=(transparent!97);
    color(69bp)=(transparent!98);
    color(72bp)=(transparent!99);
    color(75bp)=(transparent!100);
    color(100bp)=(transparent!100) }
\pgfdeclarefading{smoothpichfading}{\pgfuseshading{smoothpichshading}}

% ---- Define vertical shading, with opacity according 
%      to a logistic function
\pgfdeclareverticalshading{smoothpicvshading}{100bp}
  { color(0pt)=(transparent!0);
    color(25bp)=(transparent!0);
    color(28bp)=(transparent!1);
    color(31bp)=(transparent!2);
    color(34bp)=(transparent!3);
    color(38bp)=(transparent!6);
    color(41bp)=(transparent!11);
    color(44bp)=(transparent!20);
    color(47bp)=(transparent!33);
    color(50bp)=(transparent!50);
    color(53bp)=(transparent!67);
    color(56bp)=(transparent!80);
    color(59bp)=(transparent!89);
    color(63bp)=(transparent!94);
    color(66bp)=(transparent!97);
    color(69bp)=(transparent!98);
    color(72bp)=(transparent!99);
    color(75bp)=(transparent!100);
    color(100bp)=(transparent!100) }
\pgfdeclarefading{smoothpicvfading}{\pgfuseshading{smoothpicvshading}}

% ---- Define the \smoothpic command
\newcommand\smoothpic[2][]{%
    \bgroup%
    \begin{tikzpicture}
        % --- add node containing the image  
        \node (smoothpic) [inner sep=0.5pt] { \includegraphics[#1]{#2} };

        % --- determine height and width of the image
        \pgf@process{\pgfpointdiff{\pgfpointanchor{smoothpic}{south east}}{\pgfpointanchor{smoothpic}{north west}}}
        \setlength\smoothpic@height{\pgf@y}
        \setlength\smoothpic@width{\pgf@x}

        % --- draw left overlay
        \pgfpathrectangle{\pgfpointanchor{smoothpic}{south west}}{\pgfpoint{\smoothpicshadewidth}{\smoothpic@height}}
        \pgfsetfadingforcurrentpath{smoothpichfading}{} 
        \fill [white] (smoothpic.north west) rectangle ($(smoothpic.south west)+(\smoothpicshadewidth,0)$);

        % --- draw right overlay
        \pgfpathrectangle{\pgfpointanchor{smoothpic}{south east}}{\pgfpoint{-\smoothpicshadewidth}{\smoothpic@height}}
        \pgfsetfadingforcurrentpath{smoothpichfading}{\pgftransformrotate{180}}
        \fill [white] (smoothpic.north east) rectangle ($(smoothpic.south east)+(-\smoothpicshadewidth,0)$);

        % --- draw top overlay
        \pgfpathrectangle{\pgfpointanchor{smoothpic}{north east}}{\pgfpoint{\smoothpic@width}{-\smoothpicshadewidth}}
        \pgfsetfadingforcurrentpath{smoothpicvfading}{\pgftransformrotate{180}}
        \fill [white] (smoothpic.north west) rectangle ($(smoothpic.north east)+(0,-\smoothpicshadewidth)$);

        % --- draw bottom overlay
        \pgfpathrectangle{\pgfpointanchor{smoothpic}{south east}}{\pgfpoint{\smoothpic@width}{\smoothpicshadewidth}}
        \pgfsetfadingforcurrentpath{smoothpicvfading}{}
        \fill [white] (smoothpic.south west) rectangle ($(smoothpic.south east)+(0,\smoothpicshadewidth)$);
    \end{tikzpicture}%
    \egroup%
}

This package can be used as follows:

\documentclass{article}
\usepackage{smoothpic}
\begin{document}
\smoothpicshadewidth=24pt
\smoothpic{pic.jpg}
\end{document}

I generated the logistic opacity sequence using the following python code: (M is the number of piecewise linear parts of the opacity function, and C is a steepness parameter) I provide this code only for reference, you don't need it to compile the LaTeX code.

import math
M = 16
C = 11
for i in range(0, M+1):
    x = (float(i) / M - 0.5)
    p = round(50 + 50 * x)
    opacity = round(100* math.exp(C*x) / (1+math.exp(C*x)))
    print "    color(%dbp)=(transparent!%d);" % (p, opacity)

This renders as:

enter image description here

Which looks a bit nicer, and does not use an external pdf.

  • +1 That's a nice trick. If possible, though, I'd like to do it without an auxiliary pdf file... – jubobs May 5 '13 at 10:35
  • You can probably implement the same trick using pgf shading instead, but i don't know exactly how that works. See e.g. texample.net/tikz/examples/transparent-shadows . If no one else has a better solution by then, I might look into that later today or tomorrow. – yyzz May 5 '13 at 10:37
  • 4
    I couldn't resist to figure it out. – yyzz May 5 '13 at 12:26
  • Class. I'd upvote your answer twice if I could :) – jubobs May 5 '13 at 12:30
  • :) Thanks. I don't know how to get the "fuzzy" look in your example above though. Probably add some random disturbances to the shading, but that might be very slow. – yyzz May 5 '13 at 12:39
17

Here's a TikZ solutions that draws white rectangles over the picture:

Code

\documentclass[parskip]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{lipsum}

\newcommand{\blurrypic}[5]% pic, scale, border, iterations, opacity
{   \begin{tikzpicture}
        \node[inner sep=0pt,outer sep=0pt] (temppicnode) {\includegraphics[scale=#2]{#1}};
        \clip (temppicnode.south west) rectangle (temppicnode.north east);
        \pgfmathsetmacro{\mybmax}{sqrt(2)*#3}
        \pgfmathsetmacro{\mystep}{#3/(#4+1)}
        \pgfmathsetmacro{\myradstep}{sqrt(2)*#3/(#4+1)}
        \foreach \x [evaluate=\x as \mypred using \x-1] in {1,...,#4}
        {   \pgfmathsetmacro{\myrad}{\mybmax-(\x-1)*\myradstep}
            \fill[white,even odd rule,opacity=#5] ($(temppicnode.south west)+(-#3,-#3)$) rectangle ($(temppicnode.north east)+(#3,#3)$) [rounded corners=\myrad cm] ($(temppicnode.south west)+(\x*\mystep,\x*\mystep)$) rectangle ($(temppicnode.north east)+(-\x*\mystep,-\x*\mystep)$);
        }
        %\draw (temppicnode.north east) rectangle (temppicnode.south west);
        \fill[white,even odd rule] ($(temppicnode.south west)+(-#3,-#3)$) rectangle ($(temppicnode.north east)+(#3,#3)$) [rounded corners=#3 cm] (temppicnode.south west) rectangle (temppicnode.north east);
    \end{tikzpicture}
}

\begin{document}

\lipsum[1]

\blurrypic{jubobs.png}{0.5}{0.3}{20}{0.1}

\lipsum[2]

\end{document}

Output

enter image description here

  • Thanks! You can improve the look if you set the opacity to 0.15, e.g. \blurrypic{jubobs.png}{0.5}{0.3}{20}{0.15} – Tom Bombadil May 5 '13 at 11:32
10

With PSTricks. But it needs some other tricks to be more natural in the next edit (if possible).

enter image description here

\documentclass[pstricks]{standalone}
\SpecialCoor
\usepackage{graphicx,multido}

\newsavebox\IBox
\savebox\IBox{\includegraphics{land}}

\psset
{
    xunit=.1\wd\IBox,
    yunit=.1\ht\IBox,
}

\makeatletter
\pstVerb{/ptxu {\pst@number\psxunit div} bind def}
\pstVerb{/ptyu {\pst@number\psyunit div} bind def}
\makeatother

\begin{document}
\begin{pspicture}(10,10)
    \rput[bl](0,0){\usebox\IBox}
    \multido{\r=1.0+-.025,\n=0.0+.25}{41}{%
        \psframe[linewidth=.25pt,strokeopacity=\r,linecolor=white](!\n\space ptxu \n\space ptyu)(!10 \n\space ptxu sub 10 \n\space ptyu sub)    
    }
\end{pspicture}
\end{document}
  • Thanks. I see you have a loop in there. If I apply the method to a large number of pictures in my document, is that likely to slow down compilation significantly? – jubobs May 5 '13 at 14:24
  • @Jubobs: 41 cycles per picture of course slow down the compilation but it should not hurt much especially if they are done in PostScript level as opposed to TeX level. – kiss my armpit May 5 '13 at 14:30
3

Correct and reasonable answers how to solve this in LaTeX have already been provided.

Another approach that would still allow configuring the parameters inside LaTeX while taking advantage of external editing tools would be using shell commands invoked from LaTeX to render an image. This might be useful when the requested modifications cannot be easily recreated inside LaTeX.

Besides that, TeX/LaTeX is Turing complete, and allows file access. So in theory, anything you could do with external tools could be done with LaTeX, assuming you are willing to write the necessary code (and have enough time and hardware to handle all the overhead). I was unable to find a C-to-LaTeX or LLVM-to-LaTeX-compiler, so the required effort would probably exceed what any sane person would be willing to do just to prove a point.

  • 2
    You only say what could in theory be done, so this looks more like a comment to me, not like an answer. More so as those strategies would be possible for a wide range of questions, but offer no actual solution. – Tom Bombadil May 5 '13 at 21:55
  • 1
    @TomBombadil: Normally I would agree, however the question explicitly states "I'm not asking for a full solution here, merely for pointers." The "Turing completeness" part is definitely going to stay theory, and the shell command part together with the link is as good as it gets unless you want to tailor it to a specific tool instead of leaving the (relatively unrelated) choice to the user. – Jan Schejbal May 5 '13 at 22:44
  • I'm pretty sure ImageMagick would be of use here. – mbork May 13 '14 at 6:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.