@Appendix
@Title { Implementation of Textures }
@Tag { tex }
@Begin
The following notes detail how PostScript patterns have been
used to produce textures. See the PostScript Language
Reference Manual, second edition (PLRM), especially Section 4.9.
@PP
PostScript patterns are implemented as color spaces, whereas
from a logical point of view they are really separate entities
in the graphics state, independent of color (except that a
colored texture overrides any current color while it is in
effect). To ensure that Lout's @@SetTexture and @@SetColour
symbols have this desired independence of each other, the
following operators are defined in the Lout prologue:
@CD @Tbl
mv { 0.5vx }
bfont { Italic }
bformat { @StartHSpan @Cell i { ctr } A | @HSpan | @HSpan | @Cell D }
aformat { @Cell i { right } @Code A | @Cell @Code B |
@Cell mr { 1c } @Code "-" | @Cell D }
{
@Rowb
ma { 0i }
A { Lout-defined operator }
D { What it replaces }
rb { yes }
@Rowa
A { num }
B { LoutSetGray }
D { setgray }
@Rowa
A { num num num }
B { LoutSetRGBColor }
D { setrgbcolor }
@Rowa
A { num num num }
B { LoutSetHSBColor }
D { sethsbcolor }
@Rowa
A { num num num num }
B { LoutSetCMYKColor }
D { setcmykcolor }
@Rowa
A { p }
B { LoutSetTexture }
D { setpattern }
rb { yes }
mb { 0i }
}
These have similar signatures to the corresponding PostScript
operators shown, and the idea is to use the Lout-defined
versions where you would normally use the PostScript ones.
The first four set the color without disturbing any current
texture; the last sets the texture without disturbing
any current color. Here @Code { p } may be the PostScript
{@Code null} object, meaning no texture i.e. normal filling,
or else it must be an instantiated pattern dictionary, as
returned by @Code { makepattern }.
@PP
There are three key data types used by this code:
@BulletList
@LI { A colorspace, denoted @Code { cs }, is a PostScript
colorspace array and may have one of the following values:
@DP @RID @Tbl
mv { 0.6vx }
aformat { @Cell @Code A | @Cell B }
{
@Rowa
ma { 0i }
A { "[ /DeviceGray ]" }
B { The greyscale colorspace }
@Rowa
A { "[ /DeviceRGB ]" }
B { The RGB colorspace }
@Rowa
A { "[ /DeviceCMYK ]" }
B { The CMYK colorspace }
@Rowa
A { "[ /Pattern ]" }
B { A colored pattern }
@Rowa
mb { 0i }
A { "[ /Pattern /name ]" }
B { An uncolored pattern; @Code "/name" may be
{@Code "/DeviceGray"}, {@Code "/DeviceRGB"}, or
{@Code "/DeviceCMYK"} }
}
}
@LI { A color, denoted c, is an array containing a PostScript
non-pattern color and thus may have one of the following values:
@ID @Tbl
mv { 0.6vx }
aformat { @Cell @Code A | @Cell B }
{
@Rowa
ma { 0i }
A { "[ grey ]" }
B { A @Code "/DeviceGray" color }
@Rowa
A { "[ red green blue ]" }
B { A @Code "/DeviceRGB" color }
@Rowa
A { "[ c m y k ]" }
B { A @Code "/DeviceCMYK" color }
mb { 0i }
}
We enclose colors in an array to make it easy for us to
deal with their varying length. The array has to be unpacked
with @Code "aload" before calling {@Code setcolor}.
}
@LI { A pattern, denoted {@Code "p"}. For us, a pattern is
either the PostScript null object, meaning to fill with solid
color, or else it is a dictionary as returned by
{@Code makepattern}. When such a dictionary is installed in
the current graphics state, this code guarantees that it will
contain two extra entries:
@ID @Tbl
mv { 0.6vx }
aformat { @Cell @Code A | @Cell B }
{
@Rowa
ma { 0i }
A { "/UnderlyingColorSpace" }
B { A @Code cs as defined above }
@Rowa
A { "/UnderlyingColor" }
B { A @Code c as defined above }
mb { 0i }
}
We need these extra entries to make color independent of
texture: without them we would lose the current color when
we set a texture. Because of these variables we can't share
pattern dictionaries among graphics states. We must copy them.
}
@EndList
This representation obeys the following invariant:
@BulletList
@LI {
All components of the PostScript graphics state related to
pattern and color have defined values (e.g. there is never a
situation where we set color space but not color).
}
@LI {
If the PostScript graphics state contains a @Code "/Pattern"
colorspace, the pattern dictionary stored in the state has
@Code "/UnderlyingColorSpace" and @Code "/UnderlyingColor"
entries of types @Code cs and {@Code c}.
}
@LI {
If the graphics state contains an uncolored @Code "/Pattern"
colorspace, then the @Code "/UnderlyingColorSpace" and
@Code "/UnderlyingColor" entries of the pattern dictionary
stored in the state agree with the underlying color space
and color stored in the graphics state.
}
@EndList
And it has the following abstraction function:
@BulletList
@LI {
If the graphics state colorspace is {@Code "/Pattern"}, then
the abstract current texture is the pattern dictionary stored
in the graphics state color. If the graphics state colorspace
is not {@Code "/Pattern"}, then the abstract current texture
is {@Code null}.
}
@LI {
If the graphics state colorspace is {@Code "/Pattern"}, then the
abstract colorspace and color are the values of
@Code "/UnderlyingColorSpace" and @Code "/UnderlyingColor"
in the pattern dictionary stored in the graphics state color.
If the graphics state colorspace is not {@Code "/Pattern"},
then the abstract current colorspace and color are as returned
by @Code "currentcolorspace" and {@Code "[ currentcolor ]"}.
}
@EndList
The following functions are private helpers for the public functions:
@IndentedList
@LI @OneRow -2px @Break @F @Verbatim @Begin
% Current pattern (may be null): - LoutCurrentP p
/LoutCurrentP
{ %% -
currentcolorspace %% [ /name etc ]
0 get /Pattern eq %% bool
{ %% - (have pattern)
[ currentcolor ] %% [ comp0 ... compn p ]
dup length 1 sub get %% p
}
{ %% - (no pattern)
null %% null
} ifelse %% p
} def
@End @Verbatim
@LI @OneRow -2px @Break @F @Verbatim @Begin
% Current color and color space: - LoutCurrentCCS c cs
/LoutCurrentCCS
{
LoutCurrentP dup null eq %% p bool
{ %% null
pop [ currentcolor ] %% c
currentcolorspace %% c cs
}
{ %% p
dup %% p p
/UnderlyingColor get exch %% c p
/UnderlyingColorSpace get %% c cs
} ifelse %% c cs
} def
@End @Verbatim
@LI @OneRow -2px @Break @F @Verbatim @Begin
% Make c, cs, and p current: c cs p LoutSetCCSP -
/LoutSetCCSP
{ %% c cs p
dup null eq %% c cs p bool
{ %% c cs p (null pattern)
pop setcolorspace %% c
aload pop setcolor %% -
}
{ %% c cs p (non-null pattern)
% copy pattern dictionary
12 dict copy %% c cs p
% record cs and c in p
dup /UnderlyingColorSpace %% c cs p p /UCS
3 index put %% c cs p
dup /UnderlyingColor %% c cs p p /UC
4 index put %% c cs p
% do setcolorspace and setcolor
dup /PaintType get 1 eq %% c cs p bool
{ %% c cs p (colored pattern)
[/Pattern] setcolorspace %% c cs p
setcolor %% c cs
pop pop %% -
}
{ %% c cs p (uncolored pattern)
[ /Pattern %% c cs p [ /Pattern
4 -1 roll %% c p [ /Pattern cs
] setcolorspace %% c p
exch aload length 1 add %% p comp1 ... compm m+1
-1 roll %% comp1 ... compm p
setcolor %% -
} ifelse %% -
} ifelse %% -
} def
@End @Verbatim
@EndList
With the helper functions it's now easy to derive the colour and
texture setting commands that we are offering to our end users.
When setting the color we pass it, plus the current pattern, to
{@Code "LoutSetCCSP"}; when setting the pattern we pass it, plus
the current color, to {@Code "LoutSetCCSP"}. Note that there is
no {@Code "/DeviceHSB"}: @Code "hsb" is a variant of {@Code "rgb"}.
@IndentedList
@LI @OneRow -2px @Break @F @Verbatim @Begin
% num LoutSetGray -
/LoutSetGray
{
[ 2 1 roll ] %% c
[ /DeviceGray ] %% c cs
LoutCurrentP %% c cs p
LoutSetCCSP %% -
} def
@End @Verbatim
@LI @OneRow -2px @Break @F @Verbatim @Begin
% r g b LoutSetRGBColor -
/LoutSetRGBColor
{ %% r g b
[ 4 1 roll ] %% c
[ /DeviceRGB ] %% c cs
LoutCurrentP %% c cs p
LoutSetCCSP %% -
} def
@End @Verbatim
@LI @OneRow -2px @Break @F @Verbatim @Begin
% h s b LoutSetHSBColor -
/LoutSetHSBColor
{ %% h s b
gsave sethsbcolor %% -
currentrgbcolor grestore %% r g b
LoutSetRGBColor %% -
} def
@End @Verbatim
@LI @OneRow -2px @Break @F @Verbatim @Begin
% c m y k LoutSetRGBColor -
/LoutSetCMYKColor
{
[ 5 1 roll ] %% c
[ /DeviceCMYK ] %% c cs
LoutCurrentP %% c cs p
LoutSetCCSP %% -
} def
@End @Verbatim
@LI @OneRow -2px @Break @F @Verbatim @Begin
% p LoutSetTexture -
/LoutSetTexture
{
LoutCurrentCCS %% p c cs
3 -1 roll %% c cs p
LoutSetCCSP %% -
} def
@End @Verbatim
@EndList
All we need now is some sample textures. Textures are just pattern
dictionaries as returned by {@Code "makepattern"}. Here is
a PostScript function that appears in the Lout prologue. Its
function is to simplify the production of textures. It first
takes six parameters to specify a transformation of the texture
used to build the matrix taken by {@Code makepattern}, then
five parameters that go into the pattern dictionary.
@IndentedList
@LI @OneRow -2px @Break @F @Verbatim @Begin
% <scale> <scalex> <scaley> <rotate> <hshift> <vshift>
% <pt> <bb> <xs> <ys> <pc> LoutMakeTexture p
/LoutMakeTexture
{ %% s sx sy r h v pt bb xs ys pp
12 dict begin %% s sx sy r h v pt bb xs ys pp
/PaintProc exch def %% s sx sy r h v pt bb xs ys
/YStep exch def %% s sx sy r h v pt bb xs
/XStep exch def %% s sx sy r h v pt bb
/BBox exch def %% s sx sy r h v pt
/PaintType exch def %% s sx sy r h v
/PatternType 1 def %% s sx sy r h v
/TilingType 1 def %% s sx sy r h v
currentdict end %% s sx sy r h v p
7 1 roll %% p s sx sy r h v
matrix translate %% p s sx sy r mat1
5 1 roll %% p mat1 s sx sy r
matrix rotate %% p mat1 s sx sy mat2
4 1 roll %% p mat1 mat2 s sx sy
matrix scale %% p mat1 mat2 s mat3
exch dup matrix scale %% p mat1 mat2 mat3 mat4
matrix concatmatrix %% p mat1 mat2 mat34
matrix concatmatrix %% p mat1 mat234
matrix concatmatrix %% p mat1234
/makepattern where
{ %% p mat123 dict
pop makepattern %% p
}
{ %% p mat123
pop pop null %% null
} ifelse %% p (may be null)
} def
@End @Verbatim
@EndList
For examples of textures using {@Code LoutMakeTexture}, consult
the standard include file {@Code coltex}. There is only one
built-in texture, {@Code LoutTextureSolid}:
@IndentedList
@LI @OneRow -2px @Break @F @Verbatim @Begin
/LoutTextureSolid
{
null
LoutSetTexture
} def
@End @Verbatim
@RawEndList
@End @Appendix