summaryrefslogtreecommitdiff
path: root/doc/limn.texi
blob: 1984837b0111595b8e0a407e1e68ebbaafa922c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
@c Copyright (C) 1992, 93, 04 Free Software Foundation.
@c This is part of the GNU font utilities manual.
@c For copying conditions, see the file fontutil.texi.

@node Limn
@chapter Limn

@pindex limn
@cindex bitmap to outline conversion
@cindex outline fonts, making from bitmap
@cindex spline fitting

@cindex outline font, definition of
@cindex bitmap font, definition of
These days, fonts to be used on computers are represented in one of two
ways: as a @dfn{bitmap font}, which specifies each individual pixel in
the image of a character; and/or as an @dfn{outline font}, which
specifies the image as a collection of mathematically-specified curves.
Each method has its own advantages and disadvantages; typesetting
programs, page description languages, and output devices can generally
deal with both.

@cindex autotracing
@cindex tracing outlines on a bitmap
Limn converts a font from a bitmap to an outline by fitting curves to
the pixels.  Non-shape-related information in the bitmap font, such as
that for the side bearings, is preserved in the outline output.

Specifically, the input is a bitmap (GF or PK) font.  The output is a
BZR outline font (@pxref{BZR files}), which can then be converted to
(for example) Metafont or PostScript with BZRto (@pxref{BZRto}).

@cindex Schneider, Philip
@cindex Phoenix
@cindex Plass, Michael
@cindex Stone, Maureen
@cindex Gonczarowski, Jakob
There is a fair amount of literature on converting bitmaps to outlines.
We found three particularly helpful: Philip Schneider's Master's thesis
on his system Phoenix; Michael Plass and Maureen Stone's article
`Curve-fitting with piecewise parametric cubics' published in SIGGRAPH;
and Jakob Gonczarowski's article `A fast approach to auto-tracing (with
parametric cubics)' in the RIDT 91 conference proceedings.  See
the file @file{limn/README} for the full citations.

@menu
* Limn algorithm::              How Limn fits outlines to bitmaps.
* Invoking Limn::               Command-line options.
@end menu


@node Limn algorithm
@section Limn algorithm

@cindex Limn algorithm
@cindex algorithm for spline fitting
@cindex spline fitting, algorithm for

Limn can always (barring bugs, of course) fit some sort of outline to
the bitmap input.  But its default fit is likely to be far from the
ideal: character features may disappear, curves distorted, straight
lines turned into curves and curves into straight lines, and on and on.

To control the fitting process, you must specify options to override
Limn's defaults.  To describe those options, we must describe the
algorithm Limn uses to do the fitting, which we do in this section.  We
mention the options at the appropriate point.

The next section summarizes all the options, in alphabetical order.

@flindex fit.c
Here is a schematic of the algorithm.  The subsections below go into
detail for each step.  Except for the very first step, this is
implemented in @file{limn/fit.c}.

@example
find pixel outlines
for each pixel outline:
  find corners
  for each curve list:
    remove knees
    filter
    if too small:
      fit with straight line
    otherwise fit with spline:
      set initial t values
      find tangents
      fit with one spline
      while error > reparameterize-threshold
      if error > error-threshold
      if linearity < line-threshold
    revert bad lines
    align endpoints
@end example

@menu
* Finding pixel outlines::      Extracting the edges from the bitmap.
* Finding corners::             Finding subsections of each outline.
* Removing knees::              Removing extraneous points.
* Filtering curves::            Smoothing the outlines.
* Fitting the bitmap curve::    Doing the fitting.
* Changing splines to lines::   Use straight lines where possible.
* Changing lines to splines::   Sometimes it isn't possible.
* Aligning endpoints::          If points are close enough, line them out.
* Displaying fitting online::   Seeing the results as Limn runs.
@end menu


@node Finding pixel outlines
@subsection Finding pixel outlines

@cindex pixel outlines, finding in bitmaps
@cindex cyclic curves

The first step in the conversion from a character shape represented as a
bitmap to a list of mathematical curves is to find all the cyclical
outlines (i.e., closed curves) in the bitmap image.  The resulting list
is called a @dfn{pixel outline list}.  Each @dfn{pixel outline} in the
list consists of the pixel coordinates of each edge on the outline.

For example, the pixel outline list for an @samp{i} has two elements:
one for the dot, and one for the stem.  The pixel outline list for an
@samp{o} also has two elements: one for the outside of the shape, and
one for the inside.

@cindex outside outlines
@cindex inside outlines
@cindex clockwise ordering of outlines
@cindex filling outlines
But we must differentiate between an @dfn{outside outline} (whose
interior is to be filled with black to render the character) and an
@dfn{inside outline} (whose interior is to be filled with white).
Limn's convention is to write the pixel coordinates for outside outlines
in counterclockwise order, and those for inside outlines in clockwise
order.

@cindex type 1 outlines
This counterclockwise movement of outside outlines is required by the
Type 1 format used for PostScript fonts, which is why we adopted that
convention for Limn.

For example, consider a pixel outline consisting of a single black pixel
at the origin.  The pixel has four corners, and hence the outline will
have four coordinates.  Limn looks for starting pixels from top to
bottom, left to right, within a bitmap image.  Thus, the list of pixel
coordinates will start at (0,1) and proceed counterclockwise: (0,0)
(1,0) (1,1).  Here is a picture:

@example
start => (0,1)<-(1,1)
           |      ^
           v      |
         (0
@end example


Because finding pixel outlines does not involve approximation or
estimation, there are no options to control the process.  Put another
way, Limn will always find the correct pixel coordinates for each
outline.

Once these pixel outlines have been found, each is then processed
independently; i.e., all the remaining steps, described in the following
sections, operate on each pixel outline individually.

@flindex pxl-outline.c
@flindex edge.c
The source code for this is in @file{limn/pxl-outline.c} and
@file{lib/edge.c}.


@node Finding corners
@subsection Finding corners

@cindex corners, finding

Recall that our final goal is to fit splines, i.e., continuous curves,
to the discrete bitmap image.  To that end, Limn looks for @dfn{corners}
in each pixel outline (see the previous section)---points where the
outline makes such a sharp turn that a single curve cannot
possibly fit well.  Two corners mark the endpoints of a @dfn{curve}.

@cindex curve list, definition of
We call the result a @dfn{curve list}, i.e., a list of curves on the
pixel outline: the first curve begins at that first corner and continues
through the second corner; and so on, until the last, which begins with
the last corner found and continues through the first corner.  (Each
pixel outline is cyclic by definition; again, see the previous section.)

@cindex fitting algorithm, adjusting
The corner-finding algorithm described below works fairly well in
practice, but you will probably need to adjust the parameters it uses.
Finding good corners is perhaps the most important part of the entire
fitting algorithm: missing a corner usually leads to a sharp point in
the original image being rounded off to almost nothing; finding an
extraneous corner usually leads to an extremely ugly blob.

Here is Limn's basic strategy for guessing if a given point @math{p} is
a corner: compute the total displacement (in both @math{x} and @math{y})
for some number @math{n} of points before @math{p}; do the same for
@math{n} points after @math{p}; find the angle @math{a} between those
two vectors; if that angle is less than some threshold, @math{p} is a
corner.

@itemize @bullet

@cindex resolution, dependency of fitting algorithm on
@opindex -corner-surround
The number @math{n} of points to consider is 4 by default; you can
specify a different number with the @samp{-corner-surround} option.  If
the resolution of the input font is not 300@dmn{dpi},
@samp{-corner-surround} should almost certainly be changed
proportionately.

@opindex -corner-threshold
The threshold is 100 degrees by default; you can change this with the
@samp{-corner-threshold} option.  You can see the angles at the chosen
corners via @samp{-log}.

@end itemize

However, when Limn finds a point @var{p} whose angle is below
@samp{corner-threshold}, it won't necessarily take @var{p} as the
corner.  Instead, it continues looking for another
@samp{corner-surround} points; if it finds another point @math{q} whose
angle is less than that of @math{p}, @var{q} will become the corner.
(And then Limn looks for another @samp{corner-surround} points beyond
@var{q}, and so on.)

This continued searching prevents having two corners near each other,
which is usually wrong, if the angles at the two would-be corners are
approximately the same.  On the other hand, sometimes there are
extremely sharp turns in the outline within @samp{corner-surround}
pixels; in that case, one does want nearby corners after all.

@opindex -corner-always-threshold
So Limn has one more option, @samp{-corner-always-threshold}.  If the
angle at a point is below this value (60 degrees by default), then that
point is considered a corner, regardless of how close it is to other
corners.  The search for another corner within @samp{corner-surround}
pixels continues, however.


@node Removing knees
@subsection Removing knees

@cindex knees, removing
@cindex extra pixels, removing

For each curve in the curve list determined by the corners on the pixel
outline (see the previous section), Limn next removes
@dfn{knees}---points on the inside of the outline that form a ``right
angle'' with its predecessor and successor.  That is, either (1) its
predecessor differs only in @math{x}, and its successor only in
@math{y}; or (2) its predecessor differs only in @math{y}, and its
successor only in @math{x}.

It is hard to describe in words, but here is a picture:

@example
**
 X*
  *
@end example

@noindent The point @samp{X} is a knee, if we're moving in a clockwise
direction.

Such a ``right angle'' point can be on either the inside or the outside
of the outline.  Points on the inside do nothing useful, they just slow
things down and, more importantly, make the curve being fit less
accurate.  So we remove them.  But points on the outside help to define
the shape of the curve, so we keep those.  (For example, if @samp{X} was
moved up one diagonally, we certainly want it as a part of the curve.)

Although we haven't found a case where removing knees produces an
inferior result, there's no theory about it always helping.  Also, you
may just be curious what difference it makes (as we were when we
programmed the operation).  So Limn provides an option
@samp{-keep-knees}; if you specify it, Limn simply skips this step.


@node Filtering curves
@subsection Filtering curves

@cindex filtering curves
@cindex curves, filtering
@cindex smoothing curves

After generating the final pixel coordinates for each curve (see the
previous sections), Limn next @dfn{filters} the curves to smooth them.
Before this step, all the coordinates are on integer boundaries, which
makes the curves rather bumpy and difficult to fit well.

@opindex -filter-percent
@cindex filtering, weighting
To filter a point @math{p}, Limn does the following:

@enumerate

@item
Computes the sum of the distances of @math{n} neighbors (points before
and after @math{p}) to @math{p}.  These neighbors are always taken from
the original curve, since we don't want a newly filtered point to affect
subsequent points as we continue along the curve; that leads to strange
results.

@item
Multiplies that sum by a weight, and adds the result to @math{p}.  The
weight is one-third by default; you can change this with the
@samp{-filter-percent} option, which takes an integer between zero and
100.

@end enumerate

@cindex filter iterations
@opindex -filter-iterations
Repeatedly filtering a curve leads to even more smoothing, at the
expense of fidelity to the original.  By default, Limn filters each
curve 4 times; you can change this with the @samp{-filter-iterations}
option.

@cindex filtering, minimum size of curve for
If the curve has less than five points, filtering is omitted altogether,
since such a short curve tends to collapse down to a single point.

@opindex -filter-alternative-surround
@opindex -filter-epsilon
@opindex -filter-surround
The most important filtering parameter is the number @math{n} of
surrounding points which are used to produce the new point.  Limn has
two different possibilities for this, to keep features from disappearing
in the original curve.  Let's call these possibilities @var{n} and
@var{alt_n}; typically @var{alt_n} is smaller than @var{n}.  Limn
computes the total distance along the curve both coming into and going
out of the point @math{p} for both @var{n} and @var{alt_n} surrounding
points.  Then it computes the angles between the in and out vectors for
both.  If those two angles differ by more than some threshold (10
degrees by default; you can change it with
@opindex -filter-epsilon
the @samp{-filter-epsilon} option), then Limn uses @var{alt_n} to
compute the new point; otherwise, it uses @var{n}.

Geometrically, this means that if using @var{n} points would result in a
much different new point than using @var{alt_n}, use the latter, smaller
number, thus (hopefully) distorting the curve less.

@cindex resolution of input, dependency on
@opindex -filter-alternative-surround
@opindex -filter-surround
Limn uses 2 for @var{n} and 1 for @var{alt_n} by default.  You can use
the options @samp{-filter-surround} and
@samp{-filter-alternative-surround} to change them.  If the resolution
of the input font is not 300@dmn{dpi}, you should scale them
proportionately.  (For a 1200@dmn{dpi} font, we've had good results with
@samp{-filter-surround=12} and @samp{filter-alternative-surround= 6}.)


@node Fitting the bitmap curve
@subsection Fitting the bitmap curve

@cindex fitting bitmap curves

The steps in the previous sections are preliminary to the main fitting
process.  But once we have the final coordinates for each (bitmap)
curve, we can proceed to fit it with some kind of continuous
(mathematical) function: Limn uses both straight lines (polynomials of
degree 1) and Bezier splines (degree 3).

@cindex fitting with straight lines
@cindex lines, fitting with
To begin with, to use a spline the curve must have at least four points.
If it has fewer, we simply use the line going through its first and last
points.  (There is no point in doing a fancy ``best fit'' for this case,
since the original curve is so short.)

@cindex cubic splines, fitting with
@cindex Bezier splines, fitting with
@cindex spline representation
@cindex representation of splines
Otherwise, if the curve has four or more points, we try to fit it with
a (piece of a) Bezier cubic spline.  This spline is represented as a
starting point, an ending point, and two ``control points''.  Limn uses the
endpoints of the curve as the endpoints of the spline, and adjusts the
control points to try to match the curve.

@cindex Bezier cubics, drawing
A complete description of the geometric and mathematical properties of
Bezier cubics is beyond the scope of this document.  See a computer
graphics textbook for the details.

We will use the terms ``splines'', ``cubics'', ``Bezier splines'',
``cubic splines'', and so on interchangeably, as is common practice.
(Although Bezier splines are not the only kind of cubic splines, they
are the only kind we use.)

The sections below describe the spline-fitting process in more detail.

@menu
* Initializing t::              Initializing the parameter values.
* Finding tangents::            Computing the direction of the curve at
                                  the endpoints.
* Finding the spline::          Where are the control points?
* Reparameterization::          Changing the parameter values.
* Subdivision::                 Splitting the curve into pieces.
@end menu


@node Initializing t
@subsubsection Initializing @math{t}

@cindex initializing t
@cindex t, initializing
@cindex curve parameter initialization

Limn must have some way to relate the discrete curve made from the
original bitmap to the continuous spline being fitted to that curve.
This is done by associating another number, traditionally called
@math{t}, with each point on the curve.

@cindex t, meaning of
Imagine moving along the spline through the points on the curve.  Then
@math{t} for a point @math{p} corresponds to how far along the spline
you have traveled to get to @math{p}.  In practice, of course, the
spline does not perfectly fit all the points, and so Limn adjusts the
@math{t} values to improve the fit (@pxref{Reparameterization}).  (It
also adjusts the spline itself, as mentioned above.)

@cindex chord-length parameterization
Limn initializes the @math{t} value for each point on the curve using a
method called @dfn{chord-length parameterization}.  The details of how
this works do not affect how you use the program, so we will omit them
here.  (See the Plass & Stone article cited in @file{limn/README} if
you're curious about them.)


@node Finding tangents
@subsubsection Finding tangents

@cindex blending of adjacent curves
@cindex adjacent curves, blending
As mentioned above, Limn moves the control points on the spline
to optimally fit the bitmap.  But it cannot just move them arbitrarily,
because it must make sure that the spline fitting one part of the bitmap
blends smoothly with those fit to adjacent parts.

@cindex continuity of curves
@cindex zero-order continuity
@cindex first-order continuity
@cindex G1 continuity
Technically, this smooth blending is called @dfn{continuity}, and it
comes in degrees.  Limn is concerned with the first two degrees: zero-
and first-order.  Zero-order continuity between two curves simply means
the curves are connected; first-order geometric (G1) continuity means
the tangents to each curve at the point of connection have the same
direction.  (There are other kinds of continuity besides ``geometric'',
but they are not important for our purposes.)

Informally, this means that the final shape will not abruptly turn at
the point where two splines meet.  (Any computer graphics textbook will
discuss the properties of tangents, continuity, and splines, if you're
unfamiliar with the subject.)

@cindex control points, constraints on
To achieve G1 continuity, Limn puts the first control point of a spline
on a line going in the direction of the tangent to the start of the
spline; and it puts the second control point on a line in the direction
of the tangent to the end of the spline.  (It would be going far afield
to prove that this together with the properties of Bezier splines imply
G1 continuity, but they do.  See Schneider's thesis referenced in
@file{limn/README} for a complete mathematical treatment.)

@cindex tangents, computing
@cindex resolution of input, dependency on
@opindex -tangent-surround
For the purposes of using Limn, the important thing is that Limn must
compute the tangents to the spline at the beginning and end, and must do
so accurately in order to achieve a good fit to the bitmap.  Since Limn
has available only samples (i.e., the pixel coordinates) of the curve
being fit, it cannot compute the true tangent.  Instead, it must
approximate the tangent by looking at some number of coordinates on
either side of a point.  By default, the number is 3, but you can
specify a different number with the @samp{-tangent-surround} option.  If
the resolution of the input font is different than 300@dmn{dpi}, or if
the outline Limn fits to the bitmap seems off, you will want to scale it
proportionately.


@node Finding the spline
@subsubsection Finding the spline

@cindex error in fitting
@cindex minimizing error
At last, after all the preprocessing steps described in the previous
sections, we can actually fit a spline to the bitmap.  Subject to the
tangent constraints (see the previous section), Limn finds the spline which
minimizes the @dfn{error}---the overall distance to the pixel coordinates.

@cindex least-squares error metric
@cindex sum of squares of distances
More precisely, Limn uses a @dfn{least-squares error metric} to measure
the ``goodness'' of the fit.  This metric minimizes the sum of the
squares of the distance between each point on the bitmap curve and its
corresponding point on the fitted spline.  (It is appropriate to square
the distance because it is equally bad for the fitted spline to diverge
from the curve in a positive or negative direction.)

@cindex t parameter
The correspondence between the fitted spline and the bitmap curve is
defined by the @math{t} value that is associated with each point
(@pxref{Initializing t}).

@cindex formula for best control points
For a given set of @math{t} values and given endpoints on the spline,
the control points which minimize the least-squares metric are unique.
The formula which determines them is derived in Schneider's thesis (see
the reference in @file{limn/README}); Limn implements that formula.

Once we have the control points, we can ask how well the resulting
spline actually does fit the bitmap curve.  Limn can do two things to
improve the fit: change the @math{t} values (reparameterization); or
break the curve into two pieces and then try to fit each piece
separately (subdivision).

The following two sections describe these operations in more detail.


@node  Reparameterization
@subsubsection Reparameterization

@cindex reparameterization

@dfn{Reparameterization} changes the @math{t} value for each point
@math{p} on the bitmap curve, thus changing the place on the spline
which corresponds to @math{p}.  Given these new @math{t} values, Limn
will then fit a new spline (see the previous section) to the bitmap, one
which presumably matches it more closely.

Reparameterization is almost always a win.  Only if the initial fit
(@pxref{Initializing t}) was truly terrible will reparameterization be a
waste of time, and be omitted in favor of immediate subdivision (see the
next section).

@opindex -reparameterize-threshold
@cindex threshold for reparameterization
Limn sets the default threshold for not reparameterizing to be 30
``square pixels'' (this number is compared to the least-squares error;
see the previous section).  This is usually only exceeded in cases such
as that of an outline of @samp{o}, where one spline cannot possibly fit
the entire more-or-less oval outline.  You can change the threshold with
the option @samp{-reparameterize-threshold}.

@opindex -reparameterize-improve
@cindex improvement threshold for reparameterization
If the error is less than @samp{reparameterize-threshold}, Limn
reparameterizes and refits the curve until the difference in the error
from the last iteration is less than some percentage (10 by default; you
can change this with the option @samp{-reparameterize-improve}).

After Limn has given up reparameterization (either because the initial
fit did not meet the @samp{reparameterize-threshold}, or because the
error did not decrease by at least @samp{reparameterize-improve}), the
final error is compared to another threshold, 2.0 by default.  (You can
specify this with the option @samp{-error-threshold}.)  If the error is
larger, Limn subdivides the bitmap curve (see the next section) and fits
each piece separately.  Otherwise, Limn saves the fitted spline and goes
on to the next piece of the pixel outline.


@node Subdivision
@subsubsection Subdivision

@cindex subdivision of curves
@cindex housing developments
@cindex recursive fitting

When Limn cannot fit a bitmap curve within the @samp{error-threshold}
(see the previous section), it must subdivide the curve into two pieces
and fit each independently, applying the fitting algorithm recursively.

@cindex subdivision vs. reparameterization
@cindex reparameterization vs. subdivision

As a strategy to improve the fit, subdivision is inferior to
reparameterization, because it increases the number of splines in the
character definition.  This increases the memory required to store the
character, and also the time to render it.  However, subdivision is
unavoidable in some circumstances: for example, the outlines on an
@samp{o} cannot be fit by a single spline.

@cindex subdivision point, choosing
@cindex worst fit, in subdivision
For the initial guess of the point at which to subdivide, Limn chooses
the point of worst error---the point where the fitted spline is farthest
from the bitmap curve. Although this is usually a good choice,
minimizing the chance that further subdivision will be necessary,
occasionally it is not the best: in order to preserve straight lines,
it is better to subdivide at the point where a straight becomes a curve
if that point is close to the worst point.  For example, this happens
where a serif joins the stem.

Limn has three options to control this process:

@enumerate

@opindex -subdivide-search
@item
@samp{-subdivide-search @var{percent}} specifies how far away from the
worst point to search for a better subdivision point, as a percentage of
the total number of points in the curve; the default is 10.  If you find
Limn missing a join as a subdivision point, resulting in a straight line
becoming a curve, you probably need to increase this.

@opindex -subdivide-threshold
@item
@samp{-subdivide-threshold @var{real}}: if the distance between a point
@math{p} (within the search range) and a straight line is less than
this, subdivide at @math{p}; default is .03 pixels.

@opindex -subdivide-surround
@item
@samp{-subdivide-surround @var{unsigned}}: when calculating the linearity
of the curve surrounding a potential subdivision, use this many points; 
default is 4.

@end enumerate

Because fitting a shorter curve is easier, this process will always
terminate.  (Eventually the curve will be short enough to fit with a
straight line (@pxref{Fitting the bitmap curve}), if nothing else.)


@node Changing splines to lines
@subsection Changing splines to lines

@cindex splines to lines
@cindex lines, changing splines to

Upon accepting a fitted spline (see the previous sections), Limn checks
if a straight line would fit the curve as well.  If so, that is
preferable, since it is much faster to render straight lines than cubic
splines.

More precisely, after fitting a cubic spline to a particular (segment of
a) curve, Limn finds the straight line between the spline's endpoints,
and computes the average distance (@pxref{Finding the spline}) between
the line and the curve.  If the result is less than some threshold, 1 by
default, then the spline is provisionally (see the next section) changed
to a line.

@opindex -line-threshold
@cindex threshold, for splines to lines
You can change the theshold with the @samp{-line-threshold} option.


@node Changing lines to splines
@subsection Changing lines to splines

@cindex lines to splines
@cindex splines, changing lines to
@cindex reversion of lines to splines

Once an entire curve (i.e., the bitmap outline between two corners; see
@ref{Finding corners}) has been fit, Limn checks for straight lines that
are adjacent to splines.  Unless such lines fit the bitmap
@emph{extremely} well, they must be changed to splines.

@cindex join point of lines and splines
The reason is that the point at which the line and spline meet will be a
visible ``bump'' in the typeset character unless the two blend smoothly.
Where two splines meet, the continuity is guaranteed from the way we
constructed the splines (@pxref{Finding tangents}).  But where a line
and spline meet, nothing makes the join smooth.

For example, if the outline of a @samp{o} has been subdivided many times
(as typically happens), a spline may end up fitting just a few
pixels---so few that a line would fit just as well.  The actions
described in the previous section will therefore change the spline to a
line.  But since the adjacent parts of the @samp{o} are being fit with
curves, that line will result in a noticeable flat spot in the final
output.  So we must change it back to a spline.

@cindex threshold for line reversion
We want this reversion to be more likely for short curves than long
curves, since short curves are more likely to be the result of a small
piece of a curved shape.  So Limn divides the total distance between the
fitted line and the bitmap curve by the square of the curve length, and
compares the result to a threshold, .01 by default.  You can change this
with the @samp{-line-reversion-threshold} option.


@node Aligning endpoints
@subsection Aligning endpoints

@cindex aligning endpoints
@cindex endpoints, aligning

After fitting a mathematical outline of splines and lines to a pixel
outline (@pxref{Finding pixel outlines}), Limn aligns the endpoints on
the fitted outline.  This involves simply checking each spline to see if
its starting point and ending point (in either axis) are ``close
enough'' to each other.  If they are, then they are made equal to their
average.

This is useful because even a slight offset of the endpoints can be
produce a noticeable result, especially for straight lines and corners.

@opindex -align-threshold
By default, ``close enough'' is half a pixel.  You can change this with
the @samp{-align-threshold} option.


@node Displaying fitting online
@subsection Displaying fitting online

@cindex online display of fitted outline
@cindex displaying fitted outline online
@cindex fitted outline, displaying online

@opindex -display
While experimenting with the various fitting options listed in the
preceding sections, you may find it useful to see the results of the
fitting online.  Limn can display the filtered (@pxref{Filtering
curves}) bitmap and the fitted outline online if it is run under the X
window system and you specify @samp{-do-display}.

@opindex -display-continue
Ordinarily, Limn stops at the end of fitting every character for you to
hit return, so you have a chance to examine the result.  If you just
want to get a brief glimpse or something, you can specify
@samp{-display-continue}.  Then Limn won't stop.

@vindex DISPLAY @r{environment variable}
@cindex X server, specifying
If you specify @samp{-do-display}, you must set the environment variable
@code{DISPLAY} to the X server you want Limn to use.  For example, in
Bourne-compatible shells, you might do:

@example
DISPLAY=:0 ; export DISPLAY
@end example

The output is shown on a grid, in which each square represents several
pixels in the input.  Corners are shown as filled squares; other pixels
are shown as hollow squares.

Limn has several options that change the appearance of the online
output:

@itemize @bullet

@opindex -display-grid-size
@cindex grid lines, space between
@item @samp{-display-grid-size @var{unsigned}}
The number of expanded pixels shown between the grid lines; default is 10.

@opindex -display-pixel-size
@cindex expansion of displayed pixels
@cindex displayed pixels, expansion of
@item @samp{-display-pixel-size @var{unsigned}}
The expansion factor; i.e., each input pixel is expanded to a square
this many pixels on a side; default is 9.

@opindex -display-rectangle-size
@item @samp{-display-rectangle-size @var{unsigned}}
The pixel size; i.e., each pixel shown is a square this many pixels on a
side; default is 6.  This must be less than the
@samp{display-pixel-size}, so that black pixels don't merge into each
other.

@end itemize

@vindex geometry @r{resource}
@cindex size of Limn window
@cindex Limn window size
@flindex .Xdefaults
@cindex resources for X
@cindex X resources
You can change the size of the window Limn creates with the
@code{geometry} resource in your @file{.Xdefaults} file (see the
documentation in the file @file{mit/doc/tutorials/resources.txt} in the
X distribution if you aren't
familiar with X resources).  The class name is @code{Limn}.  For
example:

@example
Limn*geometry: 300x400-0-0
@end example

@noindent makes the window 300 pixels wide, 400 pixels high, and located
in the lower right corner of the screen.


@node Invoking Limn
@section Invoking Limn

@cindex Limn options
@cindex invocation of Limn
@cindex options for Limn

This section lists the options that Limn accepts in alphabetic
order.  The previous section described many of these options in the
context of the fitting algorithm.

@xref{Command-line options}, for general option syntax.

The root of the main input fontname is called @var{font-name} below.

@table @samp

@opindex -align-threshold
@item -align-threshold @var{real}
If either coordinate of the endpoints on a spline is closer than this,
make them the same; default is .5.  @xref{Aligning endpoints}.

@opindex -corner-always-threshold
@item -corner-always-threshold @var{angle-in-degrees}
If the angle at a pixel is less than this, it is considered a corner,
even if it is within @samp{corner-surround} pixels of another corner;
default is 60.  @xref{Finding corners}.

@opindex -corner-surround
@item -corner-surround @var{unsigned}
Number of pixels on either side of a point to consider when determining
if that point is a corner; default is 4.  @xref{Finding corners}.

@opindex -corner-threshold
@item -corner-threshold @var{angle-in-degrees}
If a pixel, its predecessor(s), and its successor(s) meet at an angle
smaller than this, it's a corner; default is 100.  @xref{Finding corners}.

@opindex -display-continue
@item -display-continue
If you specified @samp{-do-display}, do not wait for you to hit return
after displaying each character online.  @xref{Displaying fitting online}.

@opindex -display-grid-size
@item -display-grid-size @var{unsigned}
Number of expanded pixels between the grid lines; default is 10.
@xref{Displaying fitting online}.

@opindex -display-pixel-size
@item -display-pixel-size @var{unsigned}
Length of one side of the square that each pixel expands into; default
is 9.  @xref{Displaying fitting online}.

@opindex -display-rectangle-size
@item -display-rectangle-size @var{unsigned}
Length of one side of the square drawn to represent input pixels;
default is 6.  Must be less than @samp{display-pixel-size}.
@xref{Displaying fitting online}.

@opindex -do-display
@item -do-display
Show the results of the fitting in an X window, if the X server
specified in the @code{DISPLAY} environment variable can be opened.
@xref{Displaying fitting online}.

@opindex -dpi
@item -dpi @var{unsigned}
The resolution, in pixels per inch.  @xref{Common options}.

@opindex -error-threshold
@item -error-threshold @var{real}
Subdivide fitted curves that are off by more pixels than this; default
is 2.0.  @xref{Reparameterization}.

@opindex -filter-alternative-surround
@item -filter-alternative-surround @var{unsigned}
Another choice for filter-surround; default is 1.  @xref{Filtering curves}.

@opindex -filter-epsilon
@item -filter-epsilon @var{real}
If the angles using @samp{filter-surround} and
@samp{filter-alternative-surround} points differ by more than this, use
the latter; default is 10.0.  @xref{Filtering curves}.

@opindex -filter-iterations
@item -filter-iterations @var{unsigned}
Smooth the curve this many times before fitting; default is 4.
@xref{Filtering curves}.

@opindex -filter-percent
@item -filter-percent @var{percent}
When filtering, use the old point plus distance of neighbors multiplied
by this (as a percentage) to determine the new point; default is 33.
@xref{Filtering curves}.

@opindex -filter-surround
@item -filter-surround @var{unsigned}
Number of pixels on either side of a point to consider when filtering
that point; default is 2.  @xref{Filtering curves}.

@opindex -help
@item -help
Print a usage message.  @xref{Common options}.

@opindex -keep-knees
@item -keep-knees
Do not remove ``knees''---points on the inside of the outline that are
between two others.  @xref{Removing knees}.

@opindex -line-reversion-threshold
@item -line-reversion-threshold @var{real}
If a spline is closer to a straight line than this, keep it a straight
line even if it is a list with curves; default is .01 pixels.
@xref{Changing lines to splines}.

@opindex -line-threshold
@item -line-threshold @var{real}
If a spline is not more than this far away from the straight line
defined by its endpoints, then output a straight line; default is 1.
@xref{Changing splines to lines}.

@opindex -log
@item -log
Write detailed progress reports to @file{@var{font_name}.log}.

@opindex -output-file
@cindex output file, naming
@item -output-file @var{filename}
Write to @var{filename} if it has a suffix and to
@file{@var{filename}.bzr} if it doesn't.  Default is
@file{@var{font-name}.bzr}.

@opindex -range
@item -range @var{char1}-@var{char2}
Only output characters with codes between @var{char1} and @var{char2},
inclusive.  (@xref{Common options}, and @ref{Specifying character codes}.)

@opindex -reparameterize-improve
@item -reparameterize-improve @var{percent}
If reparameterization doesn't improve the fit by this much, as a
percentage, then stop reparameterizing; default is 10.
@xref{Reparameterization}.

@opindex -reparameterize-threshold
@item -reparameterize-threshold @var{real}
If an initial fit is off by more pixels than this, don't bother to
reparameterize; default is 30.  @xref{Reparameterization}.

@opindex -subdivide-search
@item -subdivide-search @var{percent}
Percentage of the curve from the initial guess for a subdivision point
to look for a better one; default is 10.  @xref{Subdivision}.

@opindex -subdivide-surround
@item -subdivide-surround @var{unsigned}
Number of points on either side of a point to consider when looking for
a subdivision point; default is 4.  @xref{Subdivision}.

@opindex -subdivide-threshold
@item -subdivide-threshold @var{real}
If a point is this close or closer to a straight line, subdivide there;
default is .03 pixels.  @xref{Subdivision}.

@opindex -tangent-surround
@item -tangent-surround @var{unsigned}
Number of points on either side of a point to consider when computing
the tangent at that point; default is 3.  @xref{Finding tangents}.

@opindex -verbose
@item -verbose
Output progress reports.

@opindex -version
@item -version
Print the version number.

@end table