--- /dev/null
+/*
+ * Relevant bits of original copyright notice from
+ * http://bjh21.me.uk/bedstead/ bedstead.c #137:
+ *
+ * [...] the file is covered by the following:
+ *
+ * Copyright (c) 2009 Ben Harris.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+/*
+ * This is a program to construct an outline font from a bitmap. It's
+ * based on the character-rounding algorithm of the Mullard SAA5050
+ * series of Teletext character generators, and thus works best on
+ * character shapes in the same style of those of the SAA5050. This
+ * file includes all of the glyphs from the SAA5050, SAA5051, SAA5052,
+ * SAA5053, SAA5054, SAA5055, SAA5056, and SAA5057. The output is a
+ * Spline Font Database file suitable for feeding to Fontforge.
+ *
+ * The character-smoothing algorithm of the SAA5050 and friends is
+ * a fairly simple means of expanding a 5x9 pixel character to 10x18
+ * pixels for use on an interlaced display. All it does is to detect
+ * 2x2 clumps of pixels containing a diagonal line and add a couple of
+ * subpixels to it, like this:
+ *
+ * . # -> . . # # -> . . # # or # . -> # # . . -> # # . .
+ * # . . . # # . # # # . # # # . . # # # .
+ * # # . . # # # . . . # # . # # #
+ * # # . . # # . . . . # # . . # #
+ *
+ * This is applied to every occurrence of these patterns, even when
+ * they overlap, and the result is that thin diagonal lines are
+ * smoothed out while other features mostly remain the same.
+ *
+ * One way of extending this towards continuity would be to repeatedly
+ * double the resolution and add more pixels to diagonals each time,
+ * but this ends up with the diagonals being much too heavy. Instead,
+ * in places where the SAA5050 would add pixels, this program adds a
+ * largeish triangle to each unfilled pixel, and removes a small
+ * triangle from each filled one, something like this:
+ *
+ * . # -> . . # # -> . . / # or # . -> # # . . -> # \ . .
+ * # . . . # # . / # / . # # # . . \ # \ .
+ * # # . . / # / . . . # # . \ # \
+ * # # . . # / . . . . # # . . \ #
+ *
+ * The position of the lines is such that on a long diagonal line, the
+ * amount of filled space is the same as in the rounded bitmap. There
+ * are a few additional complications, in that the trimming of filled
+ * pixels can leave odd gaps where a diagonal stem joins another one,
+ * so the code detects this and doesn't trim in these cases:
+ *
+ * . # # -> . . # # # # -> . . / # # # -> . . / # # #
+ * # . . . . # # # # . / # / # # . / # # # #
+ * # # . . . . / # / . . . / # / . . .
+ * # # . . . . # / . . . . # / . . . .
+ *
+ * That is the interesting part of the program, and is in the dochar()
+ * function. Most of the rest is just dull geometry to join all the
+ * bits together into a sensible outline. Much of the code is wildly
+ * inefficient -- O(n^2) algorithms aren't much of a problem when you
+ * have at most a few thousand points to deal with.
+ *
+ * A rather nice feature of the outlines produced by this program is
+ * that when rasterised at precisely 10 or 20 pixels high, they
+ * produce the input and output respectively of the character-rounding
+ * process. While there are obious additional smoothings that could
+ * be applied, doing so would probably lose this nice property.
+ *
+ * [...]
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static bool *
+blank(int size)
+{
+ return calloc(size*size, sizeof(bool));
+}
+
+static void output(bool *r, int size) {
+ int x, y;
+ for (y=0; y<size; y++) {
+ putchar('\"');
+ for (x=0; x<size; x++) {
+ putchar(r[x+y*size] ? 'A' : '.');
+ }
+ putchar('\"');
+ putchar('\n');
+ }
+}
+
+static bool *
+triangle(bool *r, int size, int startpos, bool top, bool left)
+{
+ int x, y;
+ for (y = 0; y < startpos; y++) {
+ for (x = 0; x < startpos-y; x++) {
+ int rx, ry;
+ if (top) {
+ ry = y;
+ } else {
+ ry = size-1 - y;
+ }
+ if (left) {
+ rx = x;
+ } else {
+ rx = size-1 - x;
+ }
+ r[rx+ry*size]=true;
+ }
+ }
+ return r;
+}
+
+static void
+triangles(bool *r, int size, int startpos, int bl, int br, int tr, int tl)
+{
+ if (!bl) {
+ triangle(r, size, startpos, false, true);
+ }
+ if (!br) {
+ triangle(r, size, startpos, false, false);
+ }
+ if (!tr) {
+ triangle(r, size, startpos, true, false);
+ }
+ if (!tl) {
+ triangle(r, size, startpos, true, true);
+ }
+}
+
+static void
+whitepixel(bool *r, int size, int bl, int br, int tr, int tl)
+{
+ /* wrt blackpixel(): -1 for adjacency, -1 for gridlines */
+ const int startpos = size-size/4-2;
+ triangles(r, size, startpos, bl, br, tr, tl);
+}
+
+static void
+blackpixel(bool *r, int size, int bl, int br, int tr, int tl)
+{
+ const int startpos = size/4;
+ int i;
+ triangles(r, size, startpos, bl, br, tr, tl);
+ for (i=0; i<size*size; i++) {
+ r[i] = !r[i];
+ }
+}
+
+int main(int argc, char *argv[])
+{
+#define GETPIX(x,y) (!!(iter & 1u<<((y)*3+(x))))
+#define L GETPIX(x-1, y)
+#define R GETPIX(x+1, y)
+#define U GETPIX(x, y-1)
+#define D GETPIX(x, y+1)
+#define UL GETPIX(x-1, y-1)
+#define UR GETPIX(x+1, y-1)
+#define DL GETPIX(x-1, y+1)
+#define DR GETPIX(x+1, y+1)
+
+ int iter;
+ for (iter = 0; iter < 1u<<9; iter++) {
+ int state, x = 1, y = 1;
+ if (GETPIX(x, y)) {
+ bool tl, tr, bl, br;
+
+ /* Assume filled in */
+ tl = tr = bl = br = true;
+ /* Check for diagonals */
+ if ((UL && !U && !L) || (DR && !D && !R))
+ tr = bl = false;
+ if ((UR && !U && !R) || (DL && !D && !L))
+ tl = br = false;
+ /* Avoid odd gaps */
+ if (L || UL || U) tl = true;
+ if (R || UR || U) tr = true;
+ if (L || DL || D) bl = true;
+ if (R || DR || D) br = true;
+ state = 2 + 16 + (bl | br<<1 | tr<<2 | tl<<3);
+ } else {
+ bool tl, tr, bl, br;
+
+ /* Assume clear */
+ tl = tr = bl = br = false;
+ /* white pixel -- just diagonals */
+ if (L && U && !UL) tl = true;
+ if (R && U && !UR) tr = true;
+ if (L && D && !DL) bl = true;
+ if (R && D && !DR) br = true;
+ state = 2 + (bl | br<<1 | tr<<2 | tl<<3);
+ }
+ printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ GETPIX(x,y),U,UR,R,DR,D,DL,L,UL,state);
+ }
+ {
+ const int sizes[] = {7, 15, 31};
+ int size_index;
+ for (size_index = 0;
+ size_index < sizeof(sizes)/sizeof(sizes[0]);
+ size_index++) {
+ int size = sizes[size_index], state;
+ printf("XPM\n/* width height num_colors chars_per_pixel */\n");
+ printf("\"%d %d 2 1\"\n", size, size*33);
+ printf("/* colors */\n\". c #000000\"\n\"A c #FFFFFF\"\n");
+ /* icons never used for state 0 */
+ for (state = 1; state < 34; state++) {
+ bool *r = blank(size);
+ if (state == 0) {
+ ; /* nothing */
+ } else if (state == 1) {
+ /* everything */
+ memset(r, 1, size*size*sizeof(*r));
+ } else if (state < 16+2) {
+ int bits = ~(state-2);
+ whitepixel(r, size, bits&1, bits&2, bits&4, bits&8);
+ } else {
+ int bits = state-16-2;
+ blackpixel(r, size, bits&1, bits&2, bits&4, bits&8);
+ }
+ printf("/* icon for state %d */\n", state);
+ output(r, size);
+ free(r);
+ }
+ }
+ }
+ return 0;
+}