colours.ps - Some useful colour-handling stuff in PostScript
(/home/wherever/ps/lib/colours.ps) run % if run fails with invalidaccess you may need to use gv --nosafer % to send it to a printer see include_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 paleorange rgb2gray setgray % same brightness as the paleorange /equivalentgray palegreen rgb2gray def orange 0.9 adjustbrightness setrgbcolor violet palegreen rgb2gray adjustbrightness setrgbcolor 50 100 500 170 darkblue white true -0.8 rectgradientfill 50 270 500 500 white paleblue true 0.3 rectgradientfill 250 270 50 100 yellow red 0.3 radialgradientfill /points [ 256 360 [0 359 blue] [255 359 orange] [0 60 red] [255 0 violet] ] def 0 0 1000 1414 points 2.1 pointsgradientfill [ red black darkgrey ] randomrgb setrgbcolor /my3olours [ darkgrey red orange yellow violet ] 3 randomrgbgetn def
This module implements in PostScript some RGB colours and colour-handling procedures to help an artist using PostScript.
colours.ps defines the following colours:
You can use
rgbmix and
adjustbrightness to mix yourself
any other colours that you may need, just like a painter would.
Arbitrary greys are easy to define with dup dup
, for example:
/grey42 0.42 dup dup rgbdef
rgbmix
rgbinterpolate
rectgradientfill
radialgradientfill
pointsgradientfill
ryb_compliment
colorwheel
rgbdef
rgbget
rgblength
randomrgbget
randomrgbgetn
rgb2gray
adjustbrightness
pathbboxwh
redgreen_colorblind
The above invocation leaves on the stack,
in r g b
three-number form,
a colour intermediate between colour1
and colour2
.
The arguments are:
colour1
is the first colour, in r g b
three-number form
weight1 is the numerical weight
that will be given to the first colour in the mixing
colour2
is the second colour, in r g b
three-number form
weight2 is the numerical weight
that will be given to the second colour in the mixing
The weights should add up to one,
but don't have to;
they get normalised to total 1.0 automatically.
rgbmix uses non-linear mixing,
to conform to the way the eye sees these things.
The above invocation leaves on the stack,
in r g b
three-number form,
a colour which depends on where the number a lies
in relation to the series of numbers a1,a2..aN.
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.
For example:
[ .5 paleblue .51 white 1 mediumgray ] z rgbinterpolate setrgbcolor
would transform a brownian landscape in
z into a sky with clouds ...
The above invocation, conceived as a sort of extension of rectfill,
fills the rectangle with a colour-gradient,
starting at colour startrgb and ending with endrgb.
If vertical is true, the starting colour is at the bottom
(y) and the ending colour at the top (y+height).
If vertical is false, the starting colour is on the left
(x) and the ending colour on the right (x+width).
The convexity parameter should lie betwen -1.0 and 1.0.
If it is zero, the colour is interpolated linearly;
otherwise it is interpolated quadratically, such that if convexity
is positive the colour changes fastest at the start (the bottom or left),
and if convexity
is negative the colour changes fastest towards the end (the top or right).
The above invocation, conceived in the spirit of
rectgradientfill,
fills a circular ring with a colour-gradient,
starting at colour startrgb at radius r1
and ending with endrgb at radius r2.
The convexity parameter should lie betwen -1.0 and 1.0.
If it is zero, the colour is interpolated linearly;
otherwise it is interpolated quadratically, such that if convexity
is positive the colour changes fastest at the start (r1),
and if convexity
is negative the colour changes fastest towards the end (r2).
points is an array
[ Nx Ny [ix1 iy1 r1 g1 b1]
[ix2 iy2 r2 g2 b2] ... ]
The first two elements are the number of little rectangles
into which the filled area is divided.
The other elements are arrays, with five elements describing
a little rectangle whose colour you specify:
ix and iy are the integer coordinates of the little rectangle
and can range from zero to Nx - 1 or Ny - 1
respectively, and r g b are the colour.
The ratios of Nx/Ny and width/height
should be as close as possible, to keep the little rectangles roughly square.
power should lie between 1.0 and at most perhaps 15.0.
Somewhere around 2.0 is generally a good value.
If it is low, each fixed point only influences a short range
and most of the area tends to an average colour.
If it is high, the area becomes more dominated by the locally closest point,
and so tends towards a jigsaw of pieces with straight fuzzy edges.
And if it is too high, numerical under-runs can occur
which cause a division-by-zero error.
This procedure leaves on the stack the colour r2 g2 b2
which is the complimentary colour of r g b
as understood by
a painter who is used to the Red-Yellow-Blue colour-space.
So the ryb_compliment
of red is green,
the ryb_compliment
of yellow is violet, and so on.
As far as possible, the compliment preserves the brightness and the greyness
of the input r g b
.
The compliment of the compliment
r g b ryb_compliment ryb_compliment
is very close to the original r g b
but not always completely identical.
r g b
corresponding to a colour-wheel where an angle
of 0 means red,
60 means orange, 120 means yellow, 180 means green. 240 means blue
and 300 means violet.
The above invocation causes
name_of_colour
to be defined as { r g b }
so it's like a three-dimensional version of def
.
Note that because a colour has three components, the common
/x exch def
idiom doesn't work with rgbdef;
you'll need something like this instead:
/mynewcolour 4 1 roll rgbdef
This extracts the colour black
and puts it on the stack (in r g b
form),
so it's like a three-dimensional version of get
.
This returns the number of colours in the array, namely 3 in this case, In other words, it uses length and divides by three, and is a very minimal procedure. It also converts to integer using cvi.
The above invocation chooses at random one of the
colours in the array (red, black and darkgrey in this example),
and then leaves that colour on the stack (in r g b
form).
By default it will make different a choice every time you load the page, so if you need consistency you should invoke srand first.
The function randomrgb is a synonym, retained for backward compatibility.
This invocation leaves on the stack an array of 3 random colours chosen from the given array (i.e. from red, black, darkgrey, violet and blue in this example).
The chosen colours have distinct indices in the given array, so that if the colours of the given array are all distinct, the colours of the returned array will also be distinct.
By default it will make different choices every time you load the page, so if you need consistency you should invoke srand first.
Note that the colours are left on the stack in an array, eg:
[ violet red ]
so if you want them on the stack raw and unpacked, then add
aload pop
to the end, eg:
[ red black violet green ] 2 randomrgbgetn aload pop
which leaves on the stack eg: violet red
The above invocation causes the
r g b
colour to be converted to its equivalent gray value,
which is then left on the stack.
The conversion follows the NTSC video standard, which determines how
a colour television signal is rendered on a black and white television:
gray = .3 * red + .59 * green + .11 * blue
Printers and Graphic displays differ widely, and the NTSC standard does not necessarily produce a gray value which you will perceive, on your Graphic display, as having the same luminance as the original colour. If this becomes an issue for you on some particular display, then the callibrate_rgb2gray variable will allow you to callibrate the conversion.
The above invocation causes the
r g b
colour to be adjusted in brightness until it
matches the gray
value, as judged by the
rgb2gray subroutine.
The resulting colour is left on the stack in r g b
form.
gray
; this reduces the Saturation of the colour.
An excellent discussion of the many uses of equiluminant colours
of different hues can be found in Margret Livingstone's book
Vision and Art, the Biology of Seeing.
This is the same as the standard pathbbox procedure,
except that it leaves the box on the stack in the form:
llx lly width height
ready for use by rectclip, rectfill or
rectgradientfill.
The standard pathbbox leaves it
in the form: llx lly urx ury
(a string) charpath clip
clippath pathbboxwh red orange false 0.5 rectgradientfill
This procedure overwrites the standard setrgbcolor, or restores it.
true redgreen_colorblind
causes all setrgbcolor calls to average their red and green
arguments, thus displaying everything as a red/green colourblind person
would see it. This means if you're normally-sighted you can check how
your work would be perceived by a red/green colourblind person.
false redgreen_colorblind
restores the default setrgbcolor call.
Printers and graphic displays differ widely, and the NTSC standard which is used by rgb2gray and adjustbrightness does not necessarily produce a gray value which you will perceive, on your display, as having the same luminance as the original colour. If this becomes an issue for you, a simple one-parameter adjustment is available to help you customise the formula to fit your display. Usage:
(/home/wherever/ps/lib/colours.ps) run . . . /callibrate_rgb2gray 0.4 def % between 0.0 and 1.0 orange rgb2gray setgray ...
If you set callibrate_rgb2gray to 0.0,
then the standard NTSC conversion will be performed.
The NTSC formula converts evenly mixed (greyish) colours correctly.
But the NTSC formula thinks that red
has the same luminance as
0.3 setgray
, whereas your printer or display may well show the
gray as being much darker.
As you increase the value of callibrate_rgb2gray, the purer
colours (reddish, greeninsh, blueish) will be converted lighter,
without affecting the conversion of the more neutral (greyish) colours.
For example, on my CRT,
/callibrate_rgb2gray 0.4 def
gave the best results.
If you leave callibrate_rgb2gray unset,
its default value is now 0.15
A PostScript page callibrate_rgb2gray.ps is supplied to help you measure what callibrate_rgb2gray should be to suit a particular display or printer. Various values of callibrate_rgb2gray are printed down the left side of the page; choose the value which gives each grey rectangle in the row the same luminance as its neighbouring colour rectangles. Choose the best compromise value across all the colour columns.
As mentioned, for my CRT the best setting was 0.4, and for my current monitor it is 0.2. I'd enjoy hearing what the optimum is for other devices; if they cluster around a particular value I'll make that the default.
To install: go to
www.pjb.com.au/comp/free/colours.ps.txt
and save the file to your local disc.
Rename it to colours.ps and move it into some appropriate
directory such as ~/ps/lib
. . .
Or, first change directory to where you keep your PostScript libraries:
cd /home/wherever/ps/lib/
(or wherever) and then either:
wget -O colours.ps http://www.pjb.com.au/comp/free/colours.ps.txt
or:
curl http://www.pjb.com.au/comp/free/colours.ps.txt -o colours.ps
Or, get it from gitlab:
git clone
https://gitlab.com/peterbillam/postscriptlib
Peter J Billam www.pjb.com.au/comp/contact.html
20210213 add colorwheel 20210210 add ryb_compliment 20201208 add pointsgradientfill 20200329 add redgreen_colorblind 20200130 the gray* synonyms now work at last 20191018 add grey10 grey20 grey30 grey40 grey50 grey60 grey70 grey80 grey90 20170819 add randomrgbgetn 20160629 add paleyellow 20160623 add radialgradientfill 20160519 add pathbboxwh 20160515 add darkorange 20160425 fix the small gap rectgradientfill left at the beginning 20160421 add randomrgb, useful eg. for foliage 20140703 add rectgradientfill, useful eg. for art deco and psychedelia 20060121 callibrate_rgb2gray now defaults to 0.15 20060121 adjustbrightness defends against division by zero 20031024 add callibrate_rgb2gray mechanism 20031020 add pink as synonym for palered 20031017 add adjustbrightness 20031016 add rgb2gray 20030610 some extra colours 20030313 roll in some stuff from line_drawing.ps 20030303 rgbinterpolate now working :-) 20030225 add nonlinearise and nonlinearrgbmix 20001214 rgbmix does non-linear mixing 20001213 corrected some colours after visual test 20001212 Colour-Handling Utilities for Postscript
As examples, see www.pjb.com.au/art/posters/ . . .
See also
sample1.ps.txt and
sample4.ps.txt . . .
Save the file to your local disc, rename it to sample1.ps or
sample4.ps and edit it so that the
run statement(s) near the beginning point to the directory where
line_drawing.ps and
colours.ps
are installed on your system.
Then use GhostView gv -nosafer
or equivalent
to view it. If you wish to print it out, feel free to use something like
include_run
to roll the run-files in.
For easy viewing, PDF versions of the results are in
sample1.pdf and
sample4.pdf . . .
See also
Back to P J B Computing or to www.pjb.com.au . . .