mk-colors.tex /size: 13 Kb    last modification: 2023-12-21 09:43
1% language=us
2
3\startcomponent mk-colors
4
5\environment mk-environment
6
7\chapter{Colors redone}
8
9\subject{introduction}
10
11Color support has been present in \CONTEXT\ right from the start and
12support has been gradualy extended, for instance with transparency
13and spot colors. About 10 years later we have the first major rewrite
14of this mechanism using attributes as implemented in \LUATEX.
15
16Because I needed a test file to check if all things still work as
17expected, I decided to recap the most important commands in this
18chapter.
19
20\subject{color support}
21
22The core command is \type {\definecolor}, so let's define a few
23colors:
24
25\startbuffer
26\definecolor [red]     [r=1]
27\definecolor [green]   [g=1]
28\definecolor [blue]    [b=1]
29\definecolor [yellow]  [y=1]
30\definecolor [magenta] [m=1]
31\definecolor [cyan]    [c=1]
32\stopbuffer
33
34\typebuffer \getbuffer
35
36This gives us the following colors:
37
38\showcolorcomponents[red,green,blue,,yellow,magenta,cyan]
39
40As you can see in this table, transparency is part of a color
41specification, so let's define a few transparent colors:
42
43\startbuffer
44\definecolor [t-red]   [r=1,a=1,t=.5]
45\definecolor [t-green] [g=1,a=1,t=.5]
46\definecolor [t-blue]  [b=1,a=1,t=.5]
47\stopbuffer
48
49\typebuffer \getbuffer
50
51\showcolorcomponents[t-red,t-green,t-blue]
52
53Because transparency is now separated from color, we can define
54transparent behaviour as follows:
55
56\startbuffer
57\definecolor[half-transparent] [a=1,t=.5]
58\stopbuffer
59
60\typebuffer \getbuffer
61
62Implementing process color spaces was not that complex, but spot and multitone
63colors took a bit more code.
64
65\startbuffer
66\definecolor     [parentspot]               [r=.5,g=.2,b=.8]
67\definespotcolor [childspot-1] [parentspot] [p=.7]
68\definespotcolor [childspot-2] [parentspot] [p=.4]
69\stopbuffer
70
71\typebuffer \getbuffer
72
73The three colors, two of them are spot colors, show up as follows:
74
75\showcolorcomponents[parentspot,childspot-1,childspot-2]
76
77Multitone colors can also be defined:
78
79\startbuffer
80\definespotcolor [spotone]   [red]   [p=1]
81\definespotcolor [spottwo]   [green] [p=1]
82
83\definespotcolor [spotone-t] [red]   [a=1,t=.5]
84\definespotcolor [spottwo-t] [green] [a=1,t=.5]
85
86\definemultitonecolor
87    [whatever]
88    [spotone=.5,spottwo=.5]
89    [b=.5]
90\definemultitonecolor
91    [whatever-t]
92    [spotone=.5,spottwo=.5]
93    [b=.5]
94    [a=1,t=.5]
95\stopbuffer
96
97\typebuffer \getbuffer
98
99Transparencies don't carry over:
100
101\showcolorcomponents[spotone,spottwo,spotone-t,spottwo-t,whatever,whatever-t]
102
103Transparencies combine as follows:
104
105\startbuffer
106\blackrule[width=3cm,height=1cm,color=spotone-t]\hskip-1.5cm
107\blackrule[width=3cm,height=1cm,color=spotone-t]
108\stopbuffer
109
110\typebuffer
111
112\startlinecorrection
113\dontleavehmode\getbuffer
114\stoplinecorrection
115
116We can still clone colors and overload color dynamically. I used the following
117test code for the \MKIV\ code:
118
119\startbuffer
120{\green green->red}
121\definecolor[green] [g=1]
122{\green green->green}
123\definecolor[green] [blue]
124{\green green->blue}
125\definecolor[blue] [red]
126{\green green->red}
127\setupcolors[expansion=yes]%
128\definecolor[blue] [red]
129\definecolor[green] [blue]
130\definecolor[blue] [r=1]
131{\green green->blue}
132\stopbuffer
133
134\typebuffer \getbuffer
135
136Of course palets and color groups are supported too. We seldom use
137colorgroups, but here is an example:
138
139\startbuffer
140\definecolorgroup
141  [redish]
142  [1.00:0.90:0.90,1.00:0.80:0.80,1.00:0.70:0.70,1.00:0.55:0.55,
143   1.00:0.40:0.40,1.00:0.25:0.25,1.00:0.15:0.15,0.90:0.00:0.00]
144\stopbuffer
145
146\typebuffer \getbuffer
147
148The redish color is called by number:
149
150\startbuffer
151\blackrule[width=3cm,height=1cm,depth=0pt,color=redish:1]\quad
152\blackrule[width=3cm,height=1cm,depth=0pt,color=redish:2]\quad
153\blackrule[width=3cm,height=1cm,depth=0pt,color=redish:3]
154\stopbuffer
155
156\typebuffer
157
158\startlinecorrection
159\dontleavehmode\getbuffer
160\stoplinecorrection
161
162Palets work with names:
163
164\startbuffer
165\definepalet
166  [complement]
167  [red=cyan,green=magenta,blue=yellow]
168\stopbuffer
169
170\typebuffer \getbuffer
171
172This is used as:
173
174\startbuffer
175\blackrule[width=1cm,height=1cm,depth=0pt,color=red]\quad
176\blackrule[width=1cm,height=1cm,depth=0pt,color=green]\quad
177\blackrule[width=1cm,height=1cm,depth=0pt,color=blue]\quad
178\setuppalet[complement]%
179\blackrule[width=1cm,height=1cm,depth=0pt,color=red]\quad
180\blackrule[width=1cm,height=1cm,depth=0pt,color=green]\quad
181\blackrule[width=1cm,height=1cm,depth=0pt,color=blue]
182\stopbuffer
183
184\typebuffer
185
186\startlinecorrection
187\dontleavehmode\getbuffer
188\stoplinecorrection
189
190% Rasters are still supported but normally one will use colors:
191%
192% \startbuffer
193% \raster[.5]{\blackrule[width=3cm,height=1cm]}\quad
194% \raster[.8]{\blackrule[width=3cm,height=1cm]}
195% \stopbuffer
196%
197% \typebuffer
198%
199% \startlinecorrection
200% \dontleavehmode\getbuffer
201% \stoplinecorrection
202
203Of course the real torture test is \METAPOST\ inclusion:
204
205\startbuffer
206\startMPcode
207    path p ; p := fullcircle scaled 4cm ;
208    fill p                  withcolor \MPcolor{spotone-t} ;
209    fill p shifted(2cm,0cm) withcolor \MPcolor{spottwo-t} ;
210\stopMPcode
211\stopbuffer
212
213\typebuffer
214
215These transparent color circles up as:
216
217\startlinecorrection
218\dontleavehmode\getbuffer
219\stoplinecorrection
220
221Multitone colors also work:
222
223\startbuffer
224\startMPcode
225    path p ; p := fullcircle scaled 2cm ;
226    fill p                  withcolor \MPcolor{spotone} ;
227    fill p shifted(2cm,0cm) withcolor \MPcolor{spottwo} ;
228    fill p shifted(4cm,0cm) withcolor \MPcolor{whatever} ;
229\stopMPcode
230\stopbuffer
231
232\typebuffer
233
234This gives:
235
236\startlinecorrection
237\dontleavehmode\getbuffer
238\stoplinecorrection
239
240\subject{implementation}
241
242The implementation of colors using attributes if quite different
243from the traditional method. In \MKII\ color support works okay but
244the associated code is not that clean, if only because:
245
246\startitemize[packed]
247\item we need to keep track of grouped color usage
248\item and we do that using dedicated marks (using \TEX's mark mechanism)
249\item since this has limitations, we have quite some optimizations
250\item like local (no marks) and global colors (marks)
251\item and real dirty code to push and pop color states around pages
252\item and some messy code to deal with document colors
253\item and quite some conversion macros (think of \TEX\ not having floats)
254\stopitemize
255
256Although recent versions of \PDFTEX\ have a color stack mechanism, this
257is not adequate for our usage, if only because we support more colorspaces
258than this mechanism is supposed to deal with. (The color stack mechanism is
259written with a particular macro packag ein mind.)
260
261In \MKIV\ attributes behave like colors and therefore we no longer
262need to care about what happens at pageboundaries. Also, we no
263longer have to deal with the limitations of marks. Here:
264
265\startitemize[packed]
266\item we have distributed color spaces, color itself and transparency
267\item all injection of backend code is postponed to shipout time
268\item definition and conversion is delegated to \LUA
269\stopitemize
270
271Of course the current implementation is not as nice as we would like it
272to be. This because:
273
274\startitemize[packed]
275\item support mechanism are under construction
276\item we need to support both \MKII\ and \MKIV\ in one interface
277\item backend support is yet limited
278\stopitemize
279
280Although in principle a mechanism based on attributes is much faster than
281using marks cum suis, the new implementation is slower. The main reason is
282that we need to finalize the to be shipped out box. However, since this
283task involved more than just color, we will gain back some runtime when other
284mechanisms also use attributes.
285
286\subject{complications}
287
288This paragraph is somewhat complex, so skip it when you don't feel comfortable with
289the subject of when you've never seen low level \CONTEXT\ code.
290
291Attributes behave like fonts. This means that they are kind of frozen once
292material is boxed. Consider that we define a box as follows:
293
294\starttyping
295\setbox0\hbox{default {\red red \green green} default}
296\stoptyping
297
298What do you expect to come out the next code? In \MKII\ the \quote
299{default} inside the box will be colored yellow but the internal
300red and and green words will keep their color.
301
302\starttyping
303default {\yellow yellow \box0\ yellow} default
304\stoptyping
305
306When we use fonts switches we don't expect the content of the
307box to change. So, in the following the \quote {default} texts will
308{\em not} become bold.
309
310\starttyping
311\setbox0\hbox{default {\sl slanted \bi bold italic} default}
312default {\bf bold \box0\ bold} default
313\stoptyping
314
315Future versions of \LUATEX\ will provide more control over how attributes
316are applied to boxes, but for the moment we need to fallback on a solution
317built in \MKIV:
318
319\starttyping
320default {\yellow yellow \attributedbox0\ yellow} default
321\stoptyping
322
323There is also a \type {\attributedcopy} macro. These macros signal the
324attribute resolver (that kicks in just before shipout) that this box is to
325be treated special.
326
327In \MKII\ we had a similar situation which is why we had the option (only used
328deep down in \CONTEXT) to encapsulate a bunch of code with
329
330\starttyping
331\startregistercolor[foregroundcolor]
332some macro code ... here foregroundcolor is applied ... more code
333\stopregisteringcode
334\stoptyping
335
336This is for instance used in the \type {\framed} macro. First we package the content,
337foregroundcolor is not yet applied because the injected  specials of literals can interfere
338badly, but by registering the colors the nested color calls are tricked into thinking that
339preceding and following content is colored. When packaged, we apply backgrounds, frames,
340and foregroundcolor to the whole result. Because nested colors were aware of the
341foregroundcolor they have properly reverted to this color when needed.
342
343In \MKIV\ the situation is reversed. Here we definitely need to set the foregroundcolor
344because otherwise attributes are not set and here they don't interfere at all (no extra nodes).
345For this we use the same registration macros. When the lot is packaged, applying foregroundcolor
346is ineffective because the attributes are already applied. Instead of registering we could
347have flushed the framed content using \type {\attributedbox}, but this way we can keep the
348\MKII\ and \MKIV\ code base the same.
349
350To summarize, first the na\"ive approach. Here the nested colors know how to revert, but
351the color switch can interfere with the content (since color commands inject nodes).
352
353\starttyping
354\setbox\framed\vbox
355  {\color[foregroundcolor]{packaged framed content, can have color switches}}
356\stoptyping
357
358The \MKII\ approach registers the foreground color so the nested colors
359know what to do. There is no interfering code:
360
361\starttyping
362\startregistercolor[foregroundcolor]
363\setbox\framed
364\stopregisteringcode
365\setbox\framed{\color[foregroundcolor]{\box\framed}}
366\stoptyping
367
368The registration actually sets the color, so in fact the final coloring is not
369needed (does nothing). An alternative \MKIV\ approach is the following:
370
371\starttyping
372\color
373  [foregroundcolor]
374  {\setbox\framed{packaged framed content, can have color switches}}
375\stoptyping
376
377This works ok because attributes are applied to the whole content, i.e.\
378the box. In \MKII\ this would be quite ineffective and actually result
379in weird side effects.
380
381\starttyping
382< color stack is pushed and marks are set (unless local) >
383< color special or literal sets color to foregroundcolor >
384\setbox\framed{packaged framed content, can have color switches}
385< color special or literal sets color to foregroundcolor >
386< color stack is popped and marks are set (unless local) >
387\stoptyping
388
389So, effectively we set a box, and end up with:
390
391\starttyping
392< whatsits (special, literal and.or mark) >
393< whatsits (special, literal and.or mark) >
394\stoptyping
395
396in the main vertical lost and that will interfere badly with spacing
397and friends.
398
399In \MKIV\ however, a color switch, like a font switch does not leave any
400traces, it just sets a state. Anyway, keep in mind that there are some
401rather fundamental conceptual differences between the two appoaches.
402
403Let's end with an example that demonstrates the problem. We fill two boxes:
404
405% in previous examples we may have messed up colors
406
407\definecolor[red]   [darkred]
408\definecolor[green] [darkgreen]
409\definecolor[blue]  [darkblue]
410\definecolor[yellow][darkyellow]
411
412\startbuffer
413\setbox0\hbox{RED {\blue blue} RED}
414\setbox2\hbox{RED {\blue blue} {\attributedcopy0} RED}
415\stopbuffer
416
417\typebuffer \getbuffer
418
419We will flush these in the following lines:
420
421\startbuffer
422{unset \color[red]{red \CopyMe} unset
423    \color[red]{red \hbox{red \CopyMe}} unset}
424{unset \color[red]{red \CopyMe} unset
425    {\red red \hbox{red \CopyMe}} unset}
426{unset \color[red]{red \CopyMe} unset
427    {\red red \setbox0\hbox{red \CopyMe}\box0} unset}
428{unset \color[red]{red \CopyMe} unset
429    {\hbox{\red red \CopyMe}} unset}
430{\blue blue \color[red]{red \CopyMe} blue
431    \color[red]{red \hbox{red \CopyMe}} blue}
432\stopbuffer
433
434\typebuffer
435
436\startbuffer[yes]
437\def\CopyMe{\attributedcopy2\ \copy4}
438\stopbuffer
439
440\startbuffer[no]
441\def\CopyMe{\copy2\ \copy4}
442\stopbuffer
443
444First we define \type {\CopyMe} as follows:
445
446\typebuffer[yes]
447
448This gives:
449
450\start \enableattributeinheritance \getbuffer[yes] \getbuffer \stop
451
452Compare this with:
453
454\typebuffer[no]
455
456This gives:
457
458\getbuffer[no] \getbuffer
459
460You get the picture? At least in early version of \MKIV\ you need to
461enable support for inheritance with:
462
463\starttyping
464\enableattributeinheritance
465\stoptyping
466
467\stopcomponent
468