colors-graphics.tex /size: 12 Kb    last modification: 2024-01-16 09:02
1
2% language=us runpath=texruns:manuals/colors
3
4\startcomponent colors-basics
5
6\environment colors-environment
7
8\startchapter[title=Graphics][color=darkblue]
9
10\startsection[title=Conversion]
11
12There is not that much to tell about graphics and color simply because from the
13perspective of \TEX\ a graphic is just a blob with dimensions that travels
14through the system and in the backend gets included as|-|is. This means that when
15there is a problem with an image you have to go back to the source of that image
16and fix it there.
17
18It can happen that you need to manipulate an image and in a fully automated
19workflow that can be cumbersome. For that reason \CONTEXT\ has a mechanism for
20converting graphics.
21
22\startluacode
23    context.starttabulate { "|BT|T|" }
24    context.NC() context.bold("original")
25    context.NC() context.bold("target")
26    context.NC() context.NR()
27    for k, v in table.sortedhash(figures.converters) do
28        context.NC() context(k)
29        context.NC() context("%{\\quad }t",table.sortedkeys(v))
30        context.NC() context.NR()
31    end
32    context.stoptabulate()
33\stopluacode
34
35Some of these converters are applied automatically. For instance if you include
36an \type {eps} image, \CONTEXT\ will try to convert it into a \PDF\ file and only
37do that once (unless the image changed). Of course it needs a conversion program,
38but as long as you have GhostScript, GraphicMagick and InkScape on your machine
39it should work out well.
40
41You can also define your own converters (we use a verbose variant):
42
43\starttyping
44\startluacode
45    -- of course we need options
46
47    local resolutions = {
48        [interfaces.variables.low]    = "150x150",
49        [interfaces.variables.medium] = "300x300",
50        [interfaces.variables.high]   = "600x600",
51    }
52
53    figures.programs.lowrespng = {
54        command  = "gm",
55        argument = [[convert -resample %resolution% "%oldname%" "%newname%"]],
56    }
57
58    figures.converters["png"]["lowres.png"] = function(oldname,newname,resolution)
59        runprogram (
60            figures.programs.lowrespng.command,
61            figures.programs.lowrespng.argument,
62            {
63                oldname    = oldname,
64                newname    = newname,
65                resolution = resolutions[resolution] or "150x150"
66            }
67        )
68    end
69\stopluacode
70\stoptyping
71
72Usage is as follows:
73
74\starttyping
75\externalfigure[mill.png][conversion=lowres.png]
76\stoptyping
77
78\stopsection
79
80\startsection[title=Recoloring]
81
82You can think of more complex conversions, like converting a gray scale image to
83a colored one.
84
85\startbuffer
86\startluacode
87    figures.programs.recolor = {
88        command  = "gm",
89        argument = [[convert -recolor "%color%" "%oldname%" "%newname%"]],
90    }
91
92    figures.converters["png"]["recolor.png"] =
93        function(oldname,newname,resolution,arguments)
94            figures.programs.run (
95                figures.programs.recolor.command,
96                figures.programs.recolor.argument,
97                {
98                    oldname = oldname,
99                    newname = newname,
100                    color   = arguments or ".5 0 0 .7 0 0 .9 0 0",
101                }
102            )
103        end
104\stopluacode
105\stopbuffer
106
107\typebuffer % built in so no \getbuffer
108
109\startbuffer
110\useexternalfigure[mill][mill.png][conversion=recolor.png]
111\startcombination[3*2]
112  {\externalfigure[mill][arguments=.5 0 0 .7 0 0 .9 0 0]}{\figurefilearguments}
113  {\externalfigure[mill][arguments=.7 0 0 .9 0 0 .5 0 0]}{\figurefilearguments}
114  {\externalfigure[mill][arguments=.9 0 0 .5 0 0 .7 0 0]}{\figurefilearguments}
115  {\externalfigure[mill][arguments=.5 0 0 .9 0 0 .7 0 0]}{\figurefilearguments}
116  {\externalfigure[mill][arguments=.7 0 0 .5 0 0 .9 0 0]}{\figurefilearguments}
117  {\externalfigure[mill][arguments=.9 0 0 .7 0 0 .5 0 0]}{\figurefilearguments}
118\stopcombination
119\stopbuffer
120
121This can be applied as follows. The \type {resolution} and \type {color}
122parameters get passed to the converter. This method is actually built
123in already.
124
125\typebuffer
126
127The results are shown in \in {figure} [fig:recolor]. In this case we pass the
128colors to be use in a kind of matrix notation that GraphicMagick needs.
129
130\startplacefigure[reference=fig:recolor,title={Recoloring bitmap images.}]
131    \getbuffer
132\stopplacefigure
133
134Recoloring an image this way is actually not the best solution because
135there is an internal mechanism that does the same. This trick (currently)
136only works with spot colors.
137
138\startbuffer
139\definecolor [my-blue]   [c=1,m=.38,y=0,k=.64] % pms 2965 uncoated m
140\definecolor [my-yellow] [c=0,m=.28,y=1,k=.06] % pms  124 uncoated m
141
142\definespotcolor [my-blue-100]   [my-blue]   [p=1]
143\definespotcolor [my-yellow-100] [my-yellow] [p=1]
144\definespotcolor [my-blue-50]    [my-blue]   [p=.5]
145\definespotcolor [my-yellow-50]  [my-yellow] [p=.5]
146
147\definemultitonecolor [my-mix] [my-blue=.12,my-yellow=.28] [c=.1,m=.1,y=.3,k=.1]
148\stopbuffer
149
150\typebuffer \getbuffer
151
152These colors show up as:
153
154\starttabulate[|T||]
155\NC my-blue       \NC \blackrule[color=my-blue,      width=.6\textwidth,height=5mm] \NC \NR
156\NC my-blue-50    \NC \blackrule[color=my-blue-50,   width=.6\textwidth,height=5mm] \NC \NR
157\NC my-blue-100   \NC \blackrule[color=my-blue-100,  width=.6\textwidth,height=5mm] \NC \NR
158\NC my-yellow     \NC \blackrule[color=my-yellow,    width=.6\textwidth,height=5mm] \NC \NR
159\NC my-yellow-50  \NC \blackrule[color=my-yellow-50, width=.6\textwidth,height=5mm] \NC \NR
160\NC my-yellow-100 \NC \blackrule[color=my-yellow-100,width=.6\textwidth,height=5mm] \NC \NR
161\NC my-mix        \NC \blackrule[color=my-mix,       width=.6\textwidth,height=5mm] \NC \NR
162\stoptabulate
163
164\useexternalfigure[demofig][mill.png][object=no,width=.2\textwidth]
165
166\startbuffer
167\startcombination[4*1]
168  {\externalfigure[demofig]}                      {no color}
169  {\externalfigure[demofig][color=my-mix]}        {indexed duotone}
170  {\externalfigure[demofig][color=my-blue-100]}   {spot color}
171  {\externalfigure[demofig][color=my-yellow-100]} {spot color}
172\stopcombination
173\stopbuffer
174
175\typebuffer
176
177This time we don't call an external program but we add an indexed color map to the
178image. The result can be seen in \in {figure} [fig:reindexing].
179
180\startplacefigure[reference=fig:reindexing,title={Reindexing bitmap images.}]
181    \getbuffer
182\stopplacefigure
183
184\stopsection
185
186\startsection[title=Profiles]
187
188Color profiles are used to control the printing process. There is some (limited)
189support for that built in. An example of a setup that we use in a project is the
190following:
191
192\starttyping
193\setupexternalfigures
194  [order={pdf,eps,png,jpg},
195   conversion=cmyk.pdf,
196   method=auto]
197\stoptyping
198
199So, we prefer \PDF\ vector images, if needed converted from \EPS. When there is
200no vector image we check for a \PNG\ and as last resort for a \JPG. The
201\type{method} is set to \type {auto} which means that we check if the image file
202indeed is reflected in the suffix. This is needed because in a workflow with tens
203of thousands of images there can be bad ones.
204
205The \type {conversion} parameter will make \CONTEXT\ check if there is a \type
206{cmyk.pdf} converter defined and when that is the case, it's applied. That
207specific converter will add a color profile to the image. You can set the
208profiles with:
209
210\starttyping
211\enabledirectives[graphics.conversion.rgbprofile=srgb.icc]
212\enabledirectives[graphics.conversion.cmykprofile=isocoated_v2_eci.icc]
213\stoptyping
214
215and these happens to be the defaults. You have to make sure that the files are
216present, preferable in \type{t:/texmf/colors/icc/context}. If you add profiles
217you need to make sure that \type {colorprofiles.lua} is updated accordingly.
218
219Just for completeness, in our situation, we also have set:
220
221\starttyping
222\enabledirectives[graphics.conversion.eps.cleanup.ai]
223\enabledirectives[graphics.extracheck]
224\stoptyping
225
226The first directive will make sure that confusing sections (for instance meant to
227the drawing program) are stripped from an \EPS\ file, and the second one forces
228some extra checking on the image (just to make sure that the engine doesn't exit
229on bad images).
230
231\stopsection
232
233\startsection[title=Masks]
234
235A \PNG\ bitmap image can have a mask that permits a background to shine through
236but you can also apply that effect to a regular \PNG\ image. The next examples
237use two (pre)defined masks:
238
239\startbuffer
240\registerfiguremask [mymask1] {
241    {
242        {   0, 100, 0x00 },
243        { 101, 200, 0x7F },
244        { 201, 255, 0xFF },
245    }
246}
247
248\registerfiguremask [mymask2] {
249    210
250}
251\stopbuffer
252
253% demomask = {
254%     {   0,  63,   0 },
255%     {  64, 127, 127 },
256%     { 128, 195, 195 },
257%     { 196, 255, 255 },
258% }
259
260% \registerfiguremask [mymask1] {
261%     function()
262%         return {
263%             {   0, 100, 0x00 },
264%             { 101, 200, 0x7F },
265%             { 201, 255, 0xFF },
266%         }
267%     end
268% }
269
270\typebuffer
271
272The first mask maps the (grayscale) image values onto a mask value by range while
273the second just passes a criterium. The argument to \type {\registerfiguremask}
274is a number, table or string in \LUA\ speak
275
276\getbuffer
277
278For the examples we define two colors:
279
280\startbuffer
281\definecolor[mymaskcolor1][darkred]
282\definecolor[mymaskcolor2][.75(darkblue,white)]
283\stopbuffer
284
285\typebuffer
286
287\getbuffer
288
289\startbuffer[zero]
290\externalfigure
291  [2019-sneaky-bw-lowres.png]
292  [width=\measure{combination}]
293\stopbuffer
294
295\startbuffer[one]
296\externalfigure
297  [2019-sneaky-bw-lowres.png]
298  [background=color,
299   backgroundcolor=mymaskcolor1,
300   mask=mymask1,
301   width=\measure{combination}]
302\stopbuffer
303
304\startbuffer[two]
305\externalfigure
306  [2019-sneaky-bw-lowres.png]
307  [background=color,
308   backgroundcolor=mymaskcolor2,
309   mask=mymask2,
310   width=\measure{combination}]
311\stopbuffer
312
313\startbuffer[three]
314\externalfigure
315  [2019-sneaky-bw-lowres.png]
316  [background=color,
317   backgroundcolor=mymaskcolor2,
318   mask=demomask,
319   width=\measure{combination}]
320\stopbuffer
321
322We now include two images:
323
324\typebuffer[one]
325
326and
327
328\typebuffer[two]
329
330The result is shown in \in {figure} [fig:masks:one] and shows that one has
331probably experiment a bit with the values. The first shows the original and the
332last the predefined \quote {demomask} that uses a table with four ranges.
333
334\startplacefigure[title=Masks,reference=fig:masks:one]
335    \startcombination[4*1]
336        {\inlinebuffer[zero]} {original}
337        {\inlinebuffer[one]}  {table}
338        {\inlinebuffer[two]}  {number}
339        {\inlinebuffer[three]}{demomask}
340    \stopcombination
341\stopplacefigure
342
343We can also use an image as mask. Take these three definitions:
344
345\startbuffer[one]
346\externalfigure
347  [mill.png]
348  [height=5cm]
349\stopbuffer
350
351\startbuffer[two]
352\externalfigure
353  [2019-sneaky-bw-lowres.png]
354  [height=5cm]
355\stopbuffer
356
357\startbuffer[three]
358\externalfigure
359  [mill.png]
360  [mask=2019-sneaky-bw-lowres.png,height=5cm]
361\stopbuffer
362
363\typebuffer[one,two,three]
364
365In \in {figure} [fig:masks:two] the third example has both images stacked.
366
367\startplacefigure[title=Masks,reference=fig:masks:two]
368    \startcombination[3*1]
369        {\inlinebuffer[one]}  {}
370        {\inlinebuffer[two]}  {}
371        {\inlinebuffer[three]}{}
372    \stopcombination
373\stopplacefigure
374
375Next we show how to make an image lighter or darker. For this we use the \type
376{range} key. It can be assigned a number (fraction) or a name that serves as
377lookup in a registry. As with masks these are \LUA\ definitions. AN example of a
378range definition is:
379
380\startbuffer
381\registerfigurerange [myrange] {
382    { 0.2, 1.2 }
383}
384\stopbuffer
385
386For an \RGB\ you can provide two or six values. In \in {figure} [fig:ranges:gray]
387we show a lighter, normal, darker and limited example. In \in {figure}
388[fig:ranges:color] we apply them to a \JPEG\ image.
389
390\typebuffer \getbuffer
391
392\startplacefigure[title=Ranges,reference=fig:ranges:gray]
393    \startcombination[4*1]
394        {\externalfigure[mill.png][width=\measure{combination},range=0.80]}    {\type{range=0.80}}
395        {\externalfigure[mill.png][width=\measure{combination}]}               {default}
396        {\externalfigure[mill.png][width=\measure{combination},range=1.20]}    {\type{range=1.20}}
397        {\externalfigure[mill.png][width=\measure{combination},range=myrange]} {\type{range=myrange}}
398    \stopcombination
399\stopplacefigure
400
401\startplacefigure[title=Ranges,reference=fig:ranges:color]
402    \startcombination[4*1]
403        {\externalfigure[hacker.jpg][width=\measure{combination},range=0.80]}    {\type{range=0.80}}
404        {\externalfigure[hacker.jpg][width=\measure{combination}]}               {default}
405        {\externalfigure[hacker.jpg][width=\measure{combination},range=1.20]}    {\type{range=1.20}}
406        {\externalfigure[hacker.jpg][width=\measure{combination},range=myrange]} {\type{range=myrange}}
407    \stopcombination
408\stopplacefigure
409
410\stopsection
411
412\stopchapter
413
414\stopcomponent
415