Commit 707e0524 authored by Peter Billam's avatar Peter Billam
Browse files

First commit ...

parent cc3d0bc8
# postscriptlib
A collection of utilities for writers of PostScript
A collection of utilities for writers of PostScript, including:
* The line\_drawing.ps line-drawing subroutines for artists
* The colours.ps colour-handling subroutines for artists
* The text.ps collection of text-handling subroutines
* The fonts.ps collection of some simple custom fonts
* The brownian.ps subroutines for random fractal ("Brownian Noise")
curves and landscapes, for artists to generate horizons,
cloudscapes, interestingly textured backgrounds, etc . . .
* The Perl script include\_run to include all run-files into a
PostScript file
* A comparison of Perl and PostScript as programming languages, which
would serve Perl people as an introduction to PostScript
#! /usr/bin/perl
#########################################################################
# This Perl script is Copyright (c) 2000, Peter J Billam #
# c/o P J B Computing, GPO Box 669, Hobart TAS 7001, Australia #
# #
# Permission is granted to any individual or institution to use, copy, #
# modify or redistribute this software, so long as it is not resold for #
# profit, and provided this notice is retained. Neither Peter Billam #
# nor P J B Computing make any representations about the suitability #
# of this software for any purpose. It is provided "as is", without any #
# express or implied warranty. http://www.pjb.com.au #
#########################################################################
%seen = ();
while (<>) {
if (/^([^%]*)\(([\w\/.]+)\)\s+run(.*)$/) {
my $head = $1; my $file = $2; my $tail = $3;
if ($tail && $tail!~/^\s/) { print; next; }
if ($head) { print "$head\n"; }
if ($seen{$file}) { print "$tail\n"; next; }
&do_a_file($file);
print "$tail\n";
} else {
print;
}
}
sub do_a_file { my ($file) = $_[$[];
return unless $file;
local *F;
if (! open (F, $file)) { die "can't open $file: $!\n"; }
$seen{$file}++;
while (<F>) {
if (/^([^%]*)\(([\w\/.]+)\)\s+run(.*)$/) {
my $head = $1; my $file = $2; my $tail = $3;
if ($tail && $tail!~/^\s/) { print; next; }
if ($head) { print "$head\n"; }
if ($seen{$file}) { print "$tail\n"; next; }
&do_a_file($file);
print "$tail\n";
} else {
print;
}
}
close F;
}
__END__
=pod
=head1 NAME
include_run - Include all run-files into a PostScript file
=head1 SYNOPSIS
$ grep run foo.ps
(/home/wherever/ps/lib/a_library.ps) run
$ include_run foo.ps | lpr
$ include_run foo.ps | mail -s 'Here is foo.ps' fred@bloggs.org
$ include_run foo.ps | gs -q -sDEVICE=pdfwrite -sOutputFile=foo.pdf -
=head1 DESCRIPTION
The PostScript language includes the I<run> keyword,
which allows one file to be invoked from within another.
This is similar to, for example, the I<require> instruction in Perl.
Of course, this means that any PostScript which uses the I<run> keyword
can only be viewed if the invoked file exists on the filesystem;
therefore before the PostScript can be printed, e-mailed elsewhere,
or converted to PDF to be put on a website,
the I<run> statement must be removed and the
contents of the invoked file must be included.
I<include_run> performs this task.
If a file is invoked by more than one I<run> statement
it only gets included once;
this behaviour is also similar to the I<require> instruction in Perl.
I<include_run> does recurse; in other words, the
invoked file can itself use a I<run> statement.
=head1 RESTRICTIONS
In order to dodge the general PostScript-parsing problem,
the filename must be a literal string, enclosed in brackets (),
and must be followed by its I<run> statement.
I<include_run> can not cope with even slightly obfuscated PostScript.
=head1 BUGS
If a valid I<(...) run> command is part of a literal string,
it will get included anyway.
It could be argued that this is a feature, but in fact it's a bug.
=head1 AUTHOR
Peter J Billam <peter.billam@pjb.com.au>
=head1 SEE ALSO
http://www.pjb.com.au/comp/include_run.html,
http://www.pjb.com.au/comp/line_drawing.html,
http://www.pjb.com.au/comp/colours.html,
perl(1).
=cut
%%BeginResource: procset brownian
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This Postscript is Copyright (c) 2003, Peter J Billam %
% c/o P J B Computing, GPO Box 669, Hobart TAS 7001, Australia %
% %
% Permission is granted to any individual or institution to use, copy, %
% modify or redistribute this software, so long as it is not resold for %
% profit, and provided this notice is retained. Neither Peter Billam %
% nor P J B Computing make any representations about the suitability %
% of this software for any purpose. It is provided "as is", without any %
% express or implied warranty. http://www.pjb.com.au %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Brownian Curves and Landscapes, see
% http://www.pjb.com.au/comp/brownian.html
% "The Fractal Geometry of Nature" Benoit Mandelbrot, Freeman pp251,437
% "Fractals, Chaos, Power Laws" Manfred Schroeder, Freeman pp132-133
%
% sample usage:
% (/home/pjb/ps/lib/brownian.ps) run
%
% and then, to generate a Brownian Curve:
% /redness 1.3 def % 0=whitenoise, 1=pinknoise, 2=rednoise
% /n_harmonics 100 def
% x1 x2 y_scale redness n_harmonics new_brownian_curve
%
% newpath x1 y1 moveto % plot
% /x x1 def {
% /y x brownian_curve def
% /x x delta_x add def x x2 gt { exit } if
% } loop
% the curve begins and ends at the same height, and is centered round zero
% or, to generate a Brownian Landscape:
% /redness 2.5 def % 0=whitenoise, 1=pinknoise, 2=rednoise
% x1 y1 x2 y2 z_scale redness n_harmonics new_brownian_landscape
%
% /x x1 def { % plot
% /y y1 def {
% /z x y brownian_landscape def
% newpath z 0.5 add setgray x y delta_x dup rectfill
% /y y delta_y add def y y2 gt { exit } if
% } loop
% /x x delta_x add def x x2 gt { exit } if
% } loop
% set up some of the random stuff ...
/brownian_r 0.5 1024.0 div 1024.0 div 1024.0 div def % rand scaling factor
/brownian_phase_r brownian_r 360 mul def % for 0..360
/brownian_rand_phase { rand brownian_phase_r mul } bind def
% curve stuff ...
/new_brownian_curve { % usage: x1 x2 y_scale redness N new_brownian_curve
/brownian_N exch def /brownian_redness exch def
/brownian_y_scale exch def /brownian_x2 exch def /brownian_x1 exch def
/brownian_x2mx1 brownian_x2 brownian_x1 sub def
/brownian_phases brownian_N array def % these arrays are indexed 0..(N-1)
/brownian_amplitudes brownian_N array def
% fudge y_scale according to redness (and perhaps N)
% if redness==0 mul by .1, 1 mul by 0.4, 2 mul by .7, 3 mul by .9
brownian_redness 0.0 lt {
/brownian_redness 0.0 brownian_redness sub def
} if
/brownian_fudge 0.1 brownian_redness 0.3 mul add def
brownian_fudge 1.0 gt { /brownian_fudge 1.0 def } if
/brownian_y_scale brownian_y_scale brownian_fudge mul def
/brownian_redness brownian_redness 0.5 mul def % from power to amplitude
/brownian_i 0 def {
brownian_amplitudes brownian_i
1.0 brownian_i 1 add brownian_redness exp div put
brownian_phases brownian_i brownian_rand_phase put
/brownian_i brownian_i 1 add def brownian_i brownian_N ge { exit } if
} loop
} bind def
/brownian_curve { % usage: x brownian_curve
/brownian_x exch def
/brownian_total 0.0 def /brownian_i 0 def {
/brownian_total brownian_total
brownian_x brownian_x1 sub brownian_x2mx1 div brownian_i 1 add mul
360 mul brownian_phases brownian_i get add sin
brownian_amplitudes brownian_i get mul add def
/brownian_i brownian_i 1 add def brownian_i brownian_N ge { exit } if
} loop
brownian_total brownian_y_scale mul
} bind def
% landscape stuff ...
/new_brownian_landscape { % x1 y1 x2 y2 scale redness N new_brownian_landscape
/brownian_N exch def /brownian_redness exch def /brownian_z_scale exch def
/brownian_y2 exch def /brownian_x2 exch def
/brownian_y1 exch def /brownian_x1 exch def
/brownian_x2mx1 brownian_x2 brownian_x1 sub def
/brownian_y2my1 brownian_y2 brownian_y1 sub def
% N refers to the larger dimension; the smaller one is scaled ...
brownian_x2mx1 brownian_y2my1 ge {
/brownian_Nx brownian_N ceiling cvi def
/brownian_Ny brownian_y2my1 brownian_x2mx1 div brownian_N mul
ceiling cvi def
} {
/brownian_Nx brownian_x2mx1 brownian_y2my1 div brownian_N mul
ceiling cvi def
/brownian_Ny brownian_N ceiling cvi def
} ifelse
/brownian_phases brownian_Ny 2 mul 1 add array def % 0..2Ny, 0..Nx
/brownian_amplitudes brownian_Ny 2 mul 1 add array def % 0..2Ny, 0..Nx
/brownian_iy 0 def {
brownian_iy brownian_Ny 2 mul gt { exit } if
brownian_phases brownian_iy brownian_Nx 1 add array put
brownian_amplitudes brownian_iy brownian_Nx 1 add array put
/brownian_iy brownian_iy 1 add def
} loop
% fudge z_scale according to redness (and perhaps N)
% if redness==0 mul by .1, 1 mul by 0.4, 2 mul by .7, 3 mul by .9
brownian_redness 0.0 lt {
/brownian_redness 0.0 brownian_redness sub def
} if
/brownian_fudge 0.1 brownian_redness 0.3 mul add def
brownian_fudge 1.0 gt { /brownian_fudge 1.0 def } if
/brownian_z_scale brownian_z_scale brownian_fudge mul 0.5 mul def
/brownian_redness brownian_redness 0.5 mul def % from power to amplitude
/brownian_iy 0 def {
brownian_iy brownian_Ny 2 mul gt { exit } if
/brownian_ix 0 def {
brownian_ix brownian_Nx gt {exit} if
/brownian_p brownian_rand_phase def
/brownian_a
brownian_ix 0 eq brownian_iy brownian_Ny eq and {
0.0
} {
brownian_ix dup mul brownian_iy brownian_Ny sub dup mul add 1 add
brownian_redness -0.5 mul exp % the exp includes inverse & sqrt
} ifelse def
brownian_amplitudes brownian_iy get brownian_ix
brownian_a brownian_p sin mul put
brownian_phases brownian_iy get brownian_ix brownian_rand_phase put
/brownian_ix brownian_ix 1 add def
} loop
/brownian_iy brownian_iy 1 add def
} loop
} bind def
/brownian_landscape { % usage: x y brownian_landscape
/brownian_y exch def /brownian_x exch def
/brownian_xp1 brownian_x brownian_x1 sub brownian_x2mx1 div def
/brownian_yp1 brownian_y brownian_y1 sub brownian_y2my1 div def
/brownian_total 0.0 def
/brownian_iy 0 def {
brownian_iy brownian_Ny 2 mul gt { exit } if
/brownian_yp brownian_yp1 brownian_iy brownian_Ny sub mul def
/brownian_ay brownian_amplitudes brownian_iy get def
/brownian_py brownian_phases brownian_iy get def
/brownian_ix 0 def {
brownian_ix brownian_Nx gt {exit} if
/brownian_xp brownian_xp1 brownian_ix mul def
/brownian_total brownian_total
brownian_xp brownian_yp add 360 mul
brownian_py brownian_ix get add cos
brownian_ay brownian_ix get mul add
def
/brownian_ix brownian_ix 1 add def
} loop
/brownian_iy brownian_iy 1 add def
} loop
brownian_total brownian_z_scale mul
} bind def
/brownian_island { % usage: x y brownian_island
brownian_landscape /brownian_z exch def
brownian_yp1 .1667 gt brownian_yp1 .8333 lt and
brownian_xp1 .1667 gt and brownian_xp1 .8333 lt and { % central plateau
/brownian_z brownian_z 0.5 mul def
} { % edge
brownian_yp1 .1667 gt brownian_yp1 .8333 lt and {
/brownian_z brownian_xp1 1080 mul cos -0.25 mul 0.25 add
brownian_z mul def
} {
brownian_xp1 .1667 gt brownian_xp1 .8333 lt and {
/brownian_z brownian_yp1 1080 mul cos -0.25 mul 0.25 add
brownian_z mul def
} {
/brownian_z 1.0 brownian_xp1 1080 mul cos sub
1.0 brownian_yp1 1080 mul cos sub mul 0.125 mul brownian_z mul def
} ifelse
} ifelse
} ifelse
brownian_xp1 180 mul sin brownian_yp1 180 mul sin mul .5 sub brownian_z add
} bind def
/brownian_horiz_landscape { % usage: x y brownian_horiz_landscape
% NOPE. very diagonalish.
/brownian_y exch def /brownian_x exch def
/brownian_xp1 brownian_x brownian_x1 sub brownian_x2mx1 div def
/brownian_yp1 brownian_y brownian_y1 sub brownian_y2my1 div def
/brownian_total 0.0 def
/brownian_iy 0 def {
brownian_iy brownian_Ny 2 mul gt { exit } if
/brownian_yp brownian_yp1 brownian_iy brownian_Ny sub mul def
/brownian_ay brownian_amplitudes brownian_iy get def
/brownian_py brownian_phases brownian_iy get def
/brownian_ix 0 def {
brownian_ix brownian_Nx gt {exit} if
brownian_ix brownian_iy brownian_Ny sub abs lt { % fudge
/brownian_xp brownian_xp1 brownian_ix mul def
/brownian_total brownian_total
brownian_xp brownian_yp add 360 mul
brownian_py brownian_ix get add cos
brownian_ay brownian_ix get mul add
def
} if
/brownian_ix brownian_ix 1 add def
} loop
/brownian_iy brownian_iy 1 add def
} loop
brownian_total brownian_z_scale mul 1.2 mul % fudge
} bind def
%%EndResource
%!
% http://www.acumentraining.com/acumenjournal.html
% Case statements: see April 2001
% http://www.acumentraining.com/resources.html
% http://www.acumentraining.com/resources.html#PSResources
/case { % <<dict>> key => ---
{ % We'll do this in a stopped context
2 copy known not { % Does our key not exist?
pop /Default % Replace the key with "/Default"
2 copy known not { stop } if % If "Default" is missing, exit
} if
get exec % Get the proc and execute it
} stopped
} bind def
% Sample usage
% /CompanyProcs
% <<
% (Hot Doggie Portapotties) { (PottyProc) = }
% (Monstrous Heights Real Estate) { (MonstrousProc) = }
% (Out of My Mind, Inc.) { (Crazy) = }
% /Default { (Default) = }
% >> def
%
%
% [ (Hot Doggie Portapotties)
% (Albequerque Turkey)
% (Monstrous Heights Real Estate)
% (Out of My Mind, Inc.)
% (Gorilla My Dreams Parties)
% ]
% { CompanyProcs exch case } forall
%%BeginResource: procset colours
%
% See http://www.pjb.com.au/comp/colours.html
%
% (/home/wherever/ps/lib/line_drawing.ps) run
% (/home/wherever/ps/lib/colours.ps) run
% palegreen setrgbcolor
% blue 0.6 grey 0.4 rgbmix setrgbcolor
% /darkskin paleorange 0.4 brown 0.6 rgbmix rgbdef
% darkskin setrgbcolor
% /contourcolour [ 0 blue 50 yellow 100 green 150 grey 200 white ]
% altitude rgbinterpolate rgbdef
% /equivalentgray palegreen rgb2gray def
% orange equivalentgray adjustbrightness setrgbcolor
% orange palegreen rgb2gray adjustbrightness setrgbcolor
% x y width height startrgb endrgb vertical convexity rectgradientfill
% % vertical is boolean (false means horizontal)
%
% See also line_drawing.ps, see www.pjb.com.au/comp/line_drawing.html
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This Postscript is Copyright (c) 2000, Peter J Billam %
% Permission is granted to any individual or institution to use, copy, %
% modify or redistribute this software, so long as it is not resold for %
% profit, and provided this notice is retained. It is provided "as is", %
% without any express or implied warranty. http://www.pjb.com.au %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/white { 1 1 1 } def
/lightgrey { 0.95 0.95 0.95 } def
/lightgray { 0.95 0.95 0.95 } def
/grey { 0.9 0.9 0.9 } def
/gray { 0.9 0.9 0.9 } def
/mediumgrey { 0.8 0.8 0.8 } def
/mediumgray { 0.8 0.8 0.8 } def
/darkgrey { 0.6 0.6 0.6 } def
/darkgray { 0.6 0.6 0.6 } def
/black { 0 0 0 } def
/darkred { 0.6 0 0 } def
/red { 1 0 0 } def
/palered { 1 0.75 0.75 } def
/pink { 1 0.75 0.75 } def
/darkorange { 1 0.4 0.0 } def
/orange { 1 0.7 0.0 } def
/paleorange { 1 0.85 0.75 } def
/darkbrown { 0.5 0.4 0.4 } def
/brown { 0.7 0.55 0.55 } def
/palebrown { 0.88 0.84 0.82 } def
/yellow { 1 1 0 } def
/darkgreen { 0 .6 0 } def
/green { 0 1 0 } def
/palegreen { 0.75 1 0.75 } def
/darkblue { 0 0 0.6 } def
/blue { 0 0 1 } def
/paleblue { 0.75 0.75 1 } def
/darkviolet { 0.5 0.15 0.5 } def
/violet { 0.7 0.2 0.7 } def
/paleviolet { 1 0.75 1 } def
/randomrgb { % usage: [ red black grey ] randomrgb
dup length 3.0 div 0.01 add floor rand exch round cvi mod 3 mul
/random_rgb_i exch def
dup random_rgb_i get exch
dup random_rgb_i 1 add get exch
random_rgb_i 2 add get
} bind def
/rgbget { % usage: [ red black grey ] 1 rgbget -> black
/rgbget_i exch 3 mul def
dup rgbget_i get exch
dup rgbget_i 1 add get exch
rgbget_i 2 add get
} bind def
/rgbmix { % usage : colour1 weight1 colour2 weight2 rgbmix
/rgbmix_w2 exch def /rgbmix_b2 exch def
/rgbmix_g2 exch def /rgbmix_r2 exch def
/rgbmix_w1 exch def /rgbmix_b1 exch def
/rgbmix_g1 exch def /rgbmix_r1 exch def
% normalise weights ...
rgbmix_w1 0.0 lt { /rgbmix_w1 0.0 def } if
rgbmix_w2 0.0 lt { /rgbmix_w2 0.0 def } if
/rgbmix_tot rgbmix_w1 rgbmix_w2 add def
/rgbmix_w1 rgbmix_w1 rgbmix_tot div def
/rgbmix_w2 rgbmix_w2 rgbmix_tot div def
% leave mixed colour on stack ...
rgbmix_r1 dup mul rgbmix_w1 mul rgbmix_r2 dup mul rgbmix_w2 mul add sqrt
rgbmix_g1 dup mul rgbmix_w1 mul rgbmix_g2 dup mul rgbmix_w2 mul add sqrt
rgbmix_b1 dup mul rgbmix_w1 mul rgbmix_b2 dup mul rgbmix_w2 mul add sqrt
} bind def
/rgbinterpolate { % usage: [ a1 colour1 .. aN colourN ] a rgbinterpolate
% It is expected that a1,a2..aN are in ascending order !
% If a is a1 or less, colour1 will be returned,
% if a is between a1 and a2, rgbmix is used to interpolate between them,
% If a is a2 colour2 will be returned,
% if a is between a2 and a3, rgbmix is used to interpolate between them,
% if a is aN or more, colourN will be returned. eg:
% [ .5 paleblue .51 white 1.0 mediumgray ] z rgbinterpolate setrgbcolor
% would transform a brownian landscape into sky with clouds ...
/colours_a exch def /colours_array exch def
/colours_n colours_array length def
colours_a colours_array 0 get le {
colours_array 1 get colours_array 2 get colours_array 3 get
} {
colours_a colours_array colours_n 4 sub get ge {
colours_array colours_n 3 sub get
colours_array colours_n 2 sub get
colours_array colours_n 1 sub get
} {
/colours_i 0 def {
colours_i colours_n ge { exit } if
colours_a colours_array colours_i get ge
colours_a colours_array colours_i 4 add get le and {
colours_array colours_i 1 add get
colours_array colours_i 2 add get
colours_array colours_i 3 add get
colours_array colours_i 4 add get colours_a sub
colours_array colours_i 5 add get
colours_array colours_i 6 add get
colours_array colours_i 7 add get
colours_a colours_array colours_i get sub
rgbmix exit
} if
/colours_i colours_i 4 add def
} loop
} ifelse
} ifelse
} bind def
% currentcolorspace 0 get 256 string cvs % e.g. DeviceGray or DeviceRGB
/nonlinearise { % usage: weight halfpoint nonlinearise
% weight is 0..1 halfpoint is 0..1
% weight=0 returns 0 weight=halfpoint returns 0.5 weight=1 returns 1
0.5 ln exch abs ln div exp
% BUG must defend exp against out-of-range args
} bind def
/nonlinearrgbmix { % usage : colour1 colour2 weight halfpoint nonlinearrgbmix
% eg. red blue weight .75 nonlinearrgbmix produces mostly reddish
% red blue weight .25 nonlinearrgbmix produces mostly blueish
nonlinearise dup 3 1 roll 1.0 exch sub rgbmix
} bind def
/rgbdef { % usage: /name_of_colour r g b rgbdef
/rgbdef_b exch def /rgbdef_g exch def /rgbdef_r exch def
% This is Really Ugly stuff; Postscript is not made for string handling.
/rgbdef_s 60 string def
( )
rgbdef_s copy
0 rgbdef_r 60 string cvs dup length /rgbdef_l exch def putinterval
rgbdef_s 20 rgbdef_g 40 string cvs putinterval
rgbdef_s 40 rgbdef_b 20 string cvs putinterval
rgbdef_s 60 string copy cvx def
} bind def
/callibrate_rgb2gray 0.15 def % user-settable 0..1
/rgb2gray { % usage: /equivalentgray r g b rgb2gray def
callibrate_rgb2gray 0.0 eq {
.11 mul 3 1 roll .59 mul 3 1 roll .3 mul add add
} {
/rgb2gray_b exch def /rgb2gray_g exch def /rgb2gray_r exch def
rgb2gray_r rgb2gray_g ge rgb2gray_r rgb2gray_b ge and {
/rgb2gray_g rgb2gray_r rgb2gray_g sub callibrate_rgb2gray mul
rgb2gray_g add def
/rgb2gray_b rgb2gray_r rgb2gray_b sub callibrate_rgb2gray mul
rgb2gray_b add def
} {
rgb2gray_g rgb2gray_r ge rgb2gray_g rgb2gray_b ge and {
/rgb2gray_r rgb2gray_g rgb2gray_r sub callibrate_rgb2gray mul
rgb2gray_r add def
/rgb2gray_b rgb2gray_g rgb2gray_b sub callibrate_rgb2gray mul
rgb2gray_b add def
} {
/rgb2gray_r rgb2gray_b rgb2gray_r sub callibrate_rgb2gray mul
rgb2gray_r add def
/rgb2gray_g rgb2gray_b rgb2gray_g sub callibrate_rgb2gray mul
rgb2gray_g add def
} ifelse
} ifelse
rgb2gray_r .3 mul rgb2gray_g .59 mul rgb2gray_b .11 mul add add
} ifelse
} bind def
/rgbdup { % usage: r g b rgbdup -> r g b r g b
3 copy
} bind def
/adjustbrightness { % usage: r g b graylevel adjustbrightness
10 dict begin
% find rgb2gray, and scale. If any component is >1, then scale back
% to set it =1, then mix with white to get desired graylevel.
[ /gray /b /g /r ] { exch def } forall
gray 1.0 gt { /gray 1.0 def } if
gray 0.01 lt { /gray 0.01 def } if
r g b rgb2gray gray div /a exch def
a 0.01 lt { /a 0.01 def } if
/b b a div def
/g g a div def
/r r a div def
/max r def
max g lt { /max g def } if
max b lt { /max b def } if
max 1.0 gt { % we must decrease the saturation (mix with white)
/r r max div def
/g g max div def
/b b max div def
% we mix linearly :-(
% could also 2) binary search using rgbmix or 3) explicitly
callibrate_rgb2gray .98 gt { /callibrate_rgb2gray .98 def } if
/w 1.0 gray sub
1.0 0.3 r mul .59 g mul .11 b mul add add sub
div 1.0 callibrate_rgb2gray sub div def
/r r w mul 1.0 add w sub def
/g g w mul 1.0 add w sub def
/b b w mul 1.0 add w sub def
} if
r g b % return
end
} def % not bind in case the user wants to overwrite rgb2gray ...
% 20140703
% interpolation is quadratic in x (0<x<1) : convexity*x*x + (1-convexity)*x
/rectgradientinterp { % convexity x rectgradientinterp => a*x*x + (1-a)*x
dup dup mul % convexity x x*x
3 2 roll dup % x x*x convexity convexity
neg 1.0 add 4 1 roll mul % convexity-1 x convexity*x*x
3 1 roll mul add % convexity*x*x + (1-convexity)*x
} def
/rectgradientfill {
% x y width height startrgb endrgb vertical? convexity rectgradientfill
20 dict begin