2 * Relevant bits of original copyright notice from
3 * http://bjh21.me.uk/bedstead/ bedstead.c #137:
5 * [...] the file is covered by the following:
7 * Copyright (c) 2009 Ben Harris.
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation files
11 * (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * This is a program to construct an outline font from a bitmap. It's
31 * based on the character-rounding algorithm of the Mullard SAA5050
32 * series of Teletext character generators, and thus works best on
33 * character shapes in the same style of those of the SAA5050. This
34 * file includes all of the glyphs from the SAA5050, SAA5051, SAA5052,
35 * SAA5053, SAA5054, SAA5055, SAA5056, and SAA5057. The output is a
36 * Spline Font Database file suitable for feeding to Fontforge.
38 * The character-smoothing algorithm of the SAA5050 and friends is
39 * a fairly simple means of expanding a 5x9 pixel character to 10x18
40 * pixels for use on an interlaced display. All it does is to detect
41 * 2x2 clumps of pixels containing a diagonal line and add a couple of
42 * subpixels to it, like this:
44 * . # -> . . # # -> . . # # or # . -> # # . . -> # # . .
45 * # . . . # # . # # # . # # # . . # # # .
46 * # # . . # # # . . . # # . # # #
47 * # # . . # # . . . . # # . . # #
49 * This is applied to every occurrence of these patterns, even when
50 * they overlap, and the result is that thin diagonal lines are
51 * smoothed out while other features mostly remain the same.
53 * One way of extending this towards continuity would be to repeatedly
54 * double the resolution and add more pixels to diagonals each time,
55 * but this ends up with the diagonals being much too heavy. Instead,
56 * in places where the SAA5050 would add pixels, this program adds a
57 * largeish triangle to each unfilled pixel, and removes a small
58 * triangle from each filled one, something like this:
60 * . # -> . . # # -> . . / # or # . -> # # . . -> # \ . .
61 * # . . . # # . / # / . # # # . . \ # \ .
62 * # # . . / # / . . . # # . \ # \
63 * # # . . # / . . . . # # . . \ #
65 * The position of the lines is such that on a long diagonal line, the
66 * amount of filled space is the same as in the rounded bitmap. There
67 * are a few additional complications, in that the trimming of filled
68 * pixels can leave odd gaps where a diagonal stem joins another one,
69 * so the code detects this and doesn't trim in these cases:
71 * . # # -> . . # # # # -> . . / # # # -> . . / # # #
72 * # . . . . # # # # . / # / # # . / # # # #
73 * # # . . . . / # / . . . / # / . . .
74 * # # . . . . # / . . . . # / . . . .
76 * That is the interesting part of the program, and is in the dochar()
77 * function. Most of the rest is just dull geometry to join all the
78 * bits together into a sensible outline. Much of the code is wildly
79 * inefficient -- O(n^2) algorithms aren't much of a problem when you
80 * have at most a few thousand points to deal with.
82 * A rather nice feature of the outlines produced by this program is
83 * that when rasterised at precisely 10 or 20 pixels high, they
84 * produce the input and output respectively of the character-rounding
85 * process. While there are obious additional smoothings that could
86 * be applied, doing so would probably lose this nice property.
99 return calloc(size*size, sizeof(bool));
102 static void output(bool *r, int size) {
104 for (y=0; y<size; y++) {
106 for (x=0; x<size; x++) {
107 putchar(r[x+y*size] ? 'A' : '.');
115 triangle(bool *r, int size, int startpos, bool top, bool left)
118 for (y = 0; y < startpos; y++) {
119 for (x = 0; x < startpos-y; x++) {
138 triangles(bool *r, int size, int startpos, int bl, int br, int tr, int tl)
141 triangle(r, size, startpos, false, true);
144 triangle(r, size, startpos, false, false);
147 triangle(r, size, startpos, true, false);
150 triangle(r, size, startpos, true, true);
155 whitepixel(bool *r, int size, int bl, int br, int tr, int tl)
157 /* wrt blackpixel(): -1 for adjacency, -1 for gridlines */
158 const int startpos = size-size/4-2;
159 triangles(r, size, startpos, bl, br, tr, tl);
163 blackpixel(bool *r, int size, int bl, int br, int tr, int tl)
165 const int startpos = size/4;
167 triangles(r, size, startpos, bl, br, tr, tl);
168 for (i=0; i<size*size; i++) {
173 int main(int argc, char *argv[])
175 #define GETPIX(x,y) (!!(iter & 1u<<((y)*3+(x))))
176 #define L GETPIX(x-1, y)
177 #define R GETPIX(x+1, y)
178 #define U GETPIX(x, y-1)
179 #define D GETPIX(x, y+1)
180 #define UL GETPIX(x-1, y-1)
181 #define UR GETPIX(x+1, y-1)
182 #define DL GETPIX(x-1, y+1)
183 #define DR GETPIX(x+1, y+1)
186 for (iter = 0; iter < 1u<<9; iter++) {
187 int state, x = 1, y = 1;
191 /* Assume filled in */
192 tl = tr = bl = br = true;
193 /* Check for diagonals */
194 if ((UL && !U && !L) || (DR && !D && !R))
196 if ((UR && !U && !R) || (DL && !D && !L))
199 if (L || UL || U) tl = true;
200 if (R || UR || U) tr = true;
201 if (L || DL || D) bl = true;
202 if (R || DR || D) br = true;
203 state = 2 + 16 + (bl | br<<1 | tr<<2 | tl<<3);
208 tl = tr = bl = br = false;
209 /* white pixel -- just diagonals */
210 if (L && U && !UL) tl = true;
211 if (R && U && !UR) tr = true;
212 if (L && D && !DL) bl = true;
213 if (R && D && !DR) br = true;
214 state = 2 + (bl | br<<1 | tr<<2 | tl<<3);
216 printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
217 GETPIX(x,y),U,UR,R,DR,D,DL,L,UL,state);
220 const int sizes[] = {7, 15, 31};
223 size_index < sizeof(sizes)/sizeof(sizes[0]);
225 int size = sizes[size_index], state;
226 printf("XPM\n/* width height num_colors chars_per_pixel */\n");
227 printf("\"%d %d 2 1\"\n", size, size*33);
228 printf("/* colors */\n\". c #000000\"\n\"A c #FFFFFF\"\n");
229 /* icons never used for state 0 */
230 for (state = 1; state < 34; state++) {
231 bool *r = blank(size);
234 } else if (state == 1) {
236 memset(r, 1, size*size*sizeof(*r));
237 } else if (state < 16+2) {
238 int bits = ~(state-2);
239 whitepixel(r, size, bits&1, bits&2, bits&4, bits&8);
241 int bits = state-16-2;
242 blackpixel(r, size, bits&1, bits&2, bits&4, bits&8);
244 printf("/* icon for state %d */\n", state);