diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8938314 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# kdevelop +.kde* +*.kdev4 +# other +bld/* +banner +mkfont diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..05aee74 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +PR = banner +VPATH = . +PREFIX = +CC = $(PREFIX)gcc +CX = $(PREFIX)g++ +AS = $(PREFIX)as +AR = $(PREFIX)ar +LD = $(PREFIX)ld +SIZE = $(PREFIX)size +DUMP = $(PREFIX)objdump +COPY = $(PREFIX)objcopy +BLD= ./bld/ +AFLAGS = -adhlns=$(@:%.o=%.lst) +#CFLAGS = +CFLAGS = -Os -Wall +CFLAGS+= -fno-exceptions -fno-unwind-tables -fno-stack-check +CFLAGS+= -Wa,-adhlns=$(@:%.o=%.lst) +XFLAGS = $(CFLAGS) +XFLAGS+= -fno-rtti +LFLAGS = -lsndfile +LDLIBS = +# startup musí být linkován první +SOURCES = banner.c fft.cpp main.cpp + +WIDTH = 80 +OBJS = $(addsuffix .o,$(basename $(SOURCES))) +BOBJS = $(addprefix $(BLD),$(OBJS)) + +all: $(BLD) $(PR) + +banner.c: font.h +font.h: ./mkfont + ./mkfont --width $(WIDTH) --font /usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf +./mkfont: + gcc -Os -Wall -I/usr/include/freetype2 mkfont.c -o mkfont -lfreetype +$(BLD): + mkdir $(BLD) +# build native application +$(PR): $(BOBJS) + $(CX) -o $(PR) $(BOBJS) $(LFLAGS) +$(BLD)%.o: %.c + $(CC) -std=c11 -c $(CFLAGS) -o $@ $< +$(BLD)%.o: %.cpp + $(CX) -c $(XFLAGS) -o $@ $< +$(BLD)%.o: %.s + $(AS) $(AFLAGS) -o $@ $< +clean: + rm -f $(BLD)* $(PR) +distclean: clean + rm -f ./mkfont font.h + rm -f *.vaw *.mp3 diff --git a/README.md b/README.md index 69b0f52..8c34c0f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ # Banner -Text to spectrum \ No newline at end of file +## Text to spectrum + +Manipulace s fonty je vzata z hodně starého projektu, kdy se ještě +tiskly velké nápisy na nekonečný papír (traktor). Tady je to jen +převedeno z kmitočtové oblasti pomocí FFT do časové a zapsáno jako +zvuk do souboru. Výsledek je např. v audacity cca takový : + +![Náhled](./screen.png) + +První stopa je časový průběh, druhá (copy) spektrum. +Jakákoli ztrátová komprese nebo filtrace signálu samozřejmě spektrum +více či méně poškodí, ale zpravidla to zůstane čitelné. diff --git a/banner.c b/banner.c new file mode 100644 index 0000000..457e166 --- /dev/null +++ b/banner.c @@ -0,0 +1,128 @@ +/*////////////////////////////////////////////////////////////////////////// +// BANNER PRINTING PROGRAM +// +// Martin Renters, March/2020 +// KIZARM : +// Upraveno tak, aby to šlo přeložit i v RSX11M+ (zkrácení názvů). +// Pak jsou potřeba i soubory font.h, banner.odl, banner.cmd +//////////////////////////////////////////////////////////////////////////*/ +#include +#include +#include +#include "banner.h" +/* + +#define cputc putchar +void cputs (char *s) { + while (*s) cputc (* (s++)); +} +unsigned char cgetc() { + unsigned char c = getchar(); + if (c == '\n') c = '\r'; + return c; +} + +void cgets (char *buf, int n) { + char * i; + i = fgets (buf, n, stdin); + (void) i; + buf[strlen (buf)-1] = 0; +} +*/ +#define PAGESIZE 66 +#define MAXLINE 80 + +/*////////////////////////////////////////////////////////////////////////// +// Include font data generated by mkfont +//////////////////////////////////////////////////////////////////////////*/ +#include "font.h" + +/*////////////////////////////////////////////////////////////////////////// +// Output buffer +//////////////////////////////////////////////////////////////////////////*/ +static char outbuf[FONT_MAXWIDTH+3]; /* Account for CR, LF, NUL */ +static char *obfnex; + +/*////////////////////////////////////////////////////////////////////////// +// A bit pointer, includes pointer to byte and a bit mask for the bit +//////////////////////////////////////////////////////////////////////////*/ +struct bitpointer { + unsigned char *byte; + int bit; +}; + +/*////////////////////////////////////////////////////////////////////////// +// gbits: get next n bits +//////////////////////////////////////////////////////////////////////////*/ +static unsigned int gbits (struct bitpointer *bp, int n) { + unsigned int v = 0; + unsigned char byte = *bp->byte; + for (byte = *bp->byte; n>0; n--) { + v <<= 1; + if (byte & bp->bit) v |= 1; + bp->bit >>= 1; + if (!bp->bit) { + byte = * (++bp->byte); + bp->bit = 0x80; + } + } + return v; +} + +/*////////////////////////////////////////////////////////////////////////// +// gopcod: get the next opcode. See font.h for details of encoding +//////////////////////////////////////////////////////////////////////////*/ +static unsigned char gopcod (struct bitpointer *bp) { + unsigned int v; + + v = gbits (bp, 2); + if (v == 1) v = gbits (bp, 3) + 1; + else if (v == 2) v = gbits (bp, 4) + 9; + else if (v == 3) v = gbits (bp, 6) + 25; + + return flut[v]; +} + +/*////////////////////////////////////////////////////////////////////////// +// findch: find beginning of character opcodes +//////////////////////////////////////////////////////////////////////////*/ +static void findch (char c, struct bitpointer *bp) { + bp->byte = &mfont[foffs[c-32]]; + bp->bit = 0x80; +} +/*////////////////////////////////////////////////////////////////////////// +// banchr: prints large character +//////////////////////////////////////////////////////////////////////////*/ +void banchr (const char c) { + char colors[] = " #"; + struct bitpointer bp; + int color; + unsigned char opcode; + + color = 0; + findch (c, &bp); + obfnex = outbuf; + do { + opcode = gopcod (&bp); + if (opcode == OPCODE_EOL) { + * (obfnex++) = '\r'; + * (obfnex++) = '\n'; + * (obfnex++) = 0; + cputs (outbuf); + if (FONT_DOUBLED) cputs (outbuf); + obfnex = outbuf; + color = 0; + } else if (opcode == OPCODE_REPEAT) { + cputs (outbuf); + if (FONT_DOUBLED) cputs (outbuf); + } else if (opcode != OPCODE_EOC) { + while (opcode) { + * (obfnex++) = colors[color]; + if (FONT_DOUBLED) * (obfnex++) = colors[color]; + opcode--; + } + color = (color + 1) & 1; /* No native XOR on PDP11/05 */ + } + } while (opcode != OPCODE_EOC); + cputs ("\r\n"); +} diff --git a/banner.h b/banner.h new file mode 100644 index 0000000..8502cbb --- /dev/null +++ b/banner.h @@ -0,0 +1,11 @@ +#ifndef _BANNER_H_ +#define _BANNER_H_ +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus +extern void banchr (const char c); +extern void cputs (const char * s); +#ifdef __cplusplus +}; +#endif //__cplusplus +#endif // _BANNER_H_ diff --git a/complex.h b/complex.h new file mode 100644 index 0000000..f4b5548 --- /dev/null +++ b/complex.h @@ -0,0 +1,201 @@ +// complex.h - declaration of class +// of complex number +// +// The code is property of LIBROW +// You can use it on your own +// When utilizing credit LIBROW site + +#ifndef _COMPLEX_H_ +#define _COMPLEX_H_ + +typedef double real; + +class complex { + protected: + // Internal presentation - real and imaginary parts + real m_re; + real m_im; + + public: + // Imaginary unity + //static const complex i; + //static const complex j; + + // Constructors + complex() : m_re (0.), m_im (0.) {} + complex (real re, real im) : m_re (re), m_im (im) {} + complex (real val) : m_re (val), m_im (0.) {} + + // Assignment + complex& operator= (const real val) { + m_re = val; + m_im = 0.; + return *this; + } + + // Basic operations - taking parts + real re() const { return m_re; } + real im() const { return m_im; } + + // Conjugate number + complex conjugate() const { + return complex (m_re, -m_im); + } + + // Norm + real norm() const { + return m_re * m_re + m_im * m_im; + } + + // Arithmetic operations + complex operator+ (const complex& other) const { + return complex (m_re + other.m_re, m_im + other.m_im); + } + + complex operator- (const complex& other) const { + return complex (m_re - other.m_re, m_im - other.m_im); + } + + complex operator* (const complex& other) const { + return complex (m_re * other.m_re - m_im * other.m_im, + m_re * other.m_im + m_im * other.m_re); + } + + complex operator/ (const complex& other) const { + const real denominator = other.m_re * other.m_re + other.m_im * other.m_im; + return complex ( (m_re * other.m_re + m_im * other.m_im) / denominator, + (m_im * other.m_re - m_re * other.m_im) / denominator); + } + + complex& operator+= (const complex& other) { + m_re += other.m_re; + m_im += other.m_im; + return *this; + } + + complex& operator-= (const complex& other) { + m_re -= other.m_re; + m_im -= other.m_im; + return *this; + } + + complex& operator*= (const complex& other) { + const real temp = m_re; + m_re = m_re * other.m_re - m_im * other.m_im; + m_im = m_im * other.m_re + temp * other.m_im; + return *this; + } + + complex& operator/= (const complex& other) { + const real denominator = other.m_re * other.m_re + other.m_im * other.m_im; + const real temp = m_re; + m_re = (m_re * other.m_re + m_im * other.m_im) / denominator; + m_im = (m_im * other.m_re - temp * other.m_im) / denominator; + return *this; + } + + complex& operator++ () { + ++m_re; + return *this; + } + + complex operator++ (int) { + complex temp (*this); + ++m_re; + return temp; + } + + complex& operator-- () { + --m_re; + return *this; + } + + complex operator-- (int) { + complex temp (*this); + --m_re; + return temp; + } + + complex operator+ (const real val) const { + return complex (m_re + val, m_im); + } + + complex operator- (const real val) const { + return complex (m_re - val, m_im); + } + + complex operator* (const real val) const { + return complex (m_re * val, m_im * val); + } + + complex operator/ (const real val) const { + return complex (m_re / val, m_im / val); + } + + complex& operator+= (const real val) { + m_re += val; + return *this; + } + + complex& operator-= (const real val) { + m_re -= val; + return *this; + } + + complex& operator*= (const real val) { + m_re *= val; + m_im *= val; + return *this; + } + + complex& operator/= (const real val) { + m_re /= val; + m_im /= val; + return *this; + } + + friend complex operator+ (const real left, const complex& right) { + return complex (left + right.m_re, right.m_im); + } + + friend complex operator- (const real left, const complex& right) { + return complex (left - right.m_re, -right.m_im); + } + + friend complex operator* (const real left, const complex& right) { + return complex (left * right.m_re, left * right.m_im); + } + + friend complex operator/ (const real left, const complex& right) { + const real denominator = right.m_re * right.m_re + right.m_im * right.m_im; + return complex (left * right.m_re / denominator, + -left * right.m_im / denominator); + } + + // Boolean operators + bool operator== (const complex &other) const { + return m_re == other.m_re && m_im == other.m_im; + } + + bool operator!= (const complex &other) const { + return m_re != other.m_re || m_im != other.m_im; + } + + bool operator== (const real val) const { + return m_re == val && m_im == 0.; + } + + bool operator!= (const real val) const { + return m_re != val || m_im != 0.; + } + + friend bool operator== (const real left, const complex& right) { + return left == right.m_re && right.m_im == 0.; + } + + friend bool operator!= (const real left, const complex& right) { + return left != right.m_re || right.m_im != 0.; + } +}; + +#endif diff --git a/fft.cpp b/fft.cpp new file mode 100644 index 0000000..e08da4b --- /dev/null +++ b/fft.cpp @@ -0,0 +1,105 @@ +// fft.cpp - impelementation of class +// of fast Fourier transform - FFT +// + +#include "fft.h" +#include + +// FORWARD FOURIER TRANSFORM, INPLACE VERSION +// Data - both input data and output +// N - length of input data +bool CFFT::Forward (complex * const Data) const { + // Check input parameters + if (!Data || N < 1 || N & (N - 1)) return false; + // Rearrange + Rearrange (Data); + // Call FFT implementation + Perform (Data); + // Succeeded + return true; +} +// INVERSE FOURIER TRANSFORM, INPLACE VERSION +// Data - both input data and output +// N - length of both input data and result +// Scale - if to scale result +bool CFFT::Inverse (complex * const Data, const bool Sc /* = true */) const { + // Check input parameters + if (!Data || N < 1 || N & (N - 1)) return false; + // Rearrange + Rearrange (Data); + // Call FFT implementation + Perform (Data, true); + // Scale if necessary + if (Sc) Scale (Data); + // Succeeded + return true; +} +// Inplace version of rearrange function +void CFFT::Rearrange (complex * const Data) const { + // Swap position + unsigned int Target = 0; + // Process all positions of input signal + for (unsigned int Position = 0; Position < N; ++Position) { + // Only for not yet swapped entries + if (Target > Position) { + // Swap entries + const complex Temp (Data[Target]); + Data[Target] = Data[Position]; + Data[Position] = Temp; + } + // Výpočet Target (v podstatě reverzace pořadí bitů Position) + unsigned int Mask = N; + // While bit is set + while (Target & (Mask >>= 1)) + // Drop bit + Target &= ~Mask; + // The current bit is 0 - set it + Target |= Mask; + } +} + +// FFT implementation +void CFFT::Perform (complex * const Data, const bool Inverse /* = false */) const { + complex Product; + unsigned int Step; + const real pi = Inverse ? 3.14159265358979323846 : -3.14159265358979323846; + // Iteration through dyads, quadruples, octads and so on... + for (Step = 1; Step < N; Step <<= 1) { // 1,2,...N/2 + // Jump to the next entry of the same transform factor Jump = Step * 2 + const unsigned int Jump = Step << 1; // 2,4,...N + // Angle increment + const real delta = pi / real (Step); + const real incr = 1.0 / real (Step); + // Multiplier for trigonometric recurrence + const complex Multiplier (cos (delta), sin (delta)); + // Start value for transform factor, fi = 0 + complex Factor (1.0); + real rot = 0.0; + // Iteration through groups of different transform factor + for (unsigned int Group = 0; Group < Step; ++Group) { + // Iteration within group + for (unsigned int Pair = Group; Pair < N; Pair += Jump) { + // Match position + const unsigned int Match = Pair + Step; + // Second term of two-point transform + Product = Factor * Data[Match]; + // Transform for fi + pi + Data[Match] = Data[Pair] - Product; + // Transform for fi + Data[Pair] = Data[Pair] + Product; + } + // Successive transform factor via trigonometric recurrence + // Factor = Multiplier * Factor + Factor; + Factor *= Multiplier; + rot += incr; + } + } +} + +// Scaling of inverse FFT result +void CFFT::Scale (complex *const Data) const { + const real Factor = 1.0 / real (N); + // Scale all data entries + for (unsigned int Position = 0; Position < N; ++Position) + Data[Position] *= Factor; +} diff --git a/fft.h b/fft.h new file mode 100644 index 0000000..72b09d5 --- /dev/null +++ b/fft.h @@ -0,0 +1,33 @@ +// fft.h - declaration of class +// of fast Fourier transform - FFT +// + +#ifndef _FFT_H_ +#define _FFT_H_ + +// Include complex numbers header +#include "complex.h" + +class CFFT { + public: + CFFT (const unsigned order) : N(1< +#include +#include +#include +#include +#include + +#include "banner.h" +#include "fft.h" +using namespace std; + +static constexpr int ORDER = 8; +static constexpr int TAPS = (1 << ORDER); +static constexpr int HALF = TAPS >> 1; +static constexpr int OFS = 20; + +static CFFT fft (ORDER); +static vector mono; + +void cputs (const char * s) { + const complex one (0.0, 256.0), zero (0.0, 0.0); + complex data [TAPS]; + memset (&data,0, TAPS * sizeof(complex)); + int n = 0; + while (true) { + const char c = s [n]; + if (c == '\0') break; + const complex fill = c == '#' ? one : zero; + const int k = n + OFS; + data[k] = fill; + data[TAPS - k] = fill; + putchar(c); + n += 1; + } + //printf("\tlen=%d\n", n); + fft.Inverse(data, false); + for (int i=0; i max) max = e; + } + printf("min=%g, max=%g\n", min, max); + double z = abs(min) > abs(max) ? abs(min) : abs(max); + z = 32000.0 / z; + vector data (mono.size()); + int n = 0; + for (double e: mono) { + data[n] = ::lround (e * z); + n += 1; + } + + SF_INFO info; + memset (&info, 0, sizeof(SF_INFO)); + info.channels = 1; + info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + info.samplerate = 8000; + if (sf_format_check (&info) == SF_FALSE) { + fprintf(stderr, "bad output format\n"); + return false; + } + SNDFILE * outfile = sf_open (filename, SFM_WRITE, &info); + if (!outfile) { + fprintf(stderr, "cannot open output file \"%s\"\n", filename); + return false; + } + sf_write_short (outfile, data.data(), data.size()); + sf_close (outfile); + return true; +} +int main (int argc, char *argv[]) { + if (argc < 2) { + printf("Usage:\n\t%s \"Text to spectrogram\"\n", argv[0]); + return 0; + } + const char * buffer = argv[1]; + for (const char * p = buffer; *p; p++) { + banchr (*p); + } + WriteSound (); + return 0; +} + diff --git a/mkfont.c b/mkfont.c new file mode 100644 index 0000000..266e71e --- /dev/null +++ b/mkfont.c @@ -0,0 +1,381 @@ +#include +#include +#include +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_TRUETYPE_DRIVER_H +#include FT_MODULE_H +#include + +#define OPCODE_REPEAT 253 +#define OPCODE_EOL 254 +#define OPCODE_EOC 255 + +#define MAX_RLE 80 /* Maximum number of repeats */ + + +/**************************************************************************** +* BYTEPOINTER FUNCTIONS +****************************************************************************/ +struct histogram { + int position; + int count; +}; +struct bytepointer { + unsigned char *p; + unsigned char *start; + unsigned char *end; + struct histogram histogram[256]; +}; + +struct bitoutput { + FILE *fp; + int col; + int byte_count; + uint8_t byte; + uint8_t bit; +}; + +void init_bytepointer (struct bytepointer *bp, unsigned char *buffer, int n) { + int i; + bp->p = buffer; + bp->start = buffer; + bp->end = buffer + n; + for (i=0; i<256; i++) { + bp->histogram[i].position = i; + bp->histogram[i].count = 0; + } +} + +void save_byte (struct bytepointer *bp, unsigned char byte) { + if (bp->p == bp->end) { + printf ("OUT OF BITPOINTER MEMORY.\n"); + exit (2); + } + + // If byte is more than MAX_RLE, break into MAX_RLE, 0, remainder + while (byte < OPCODE_REPEAT && byte > MAX_RLE) { + * (bp->p++) = MAX_RLE; + * (bp->p++) = 0; + byte -= MAX_RLE; + } + * (bp->p++) = byte; + bp->histogram[byte].count++; +} + +int histcmp (const void *p, const void *q) { + struct histogram *p0 = (struct histogram *) p; + struct histogram *q0 = (struct histogram *) q; + return q0->count - p0->count; +} + +void save_bits (struct bitoutput *bits, int bit) { + if (bit) bits->byte |= bits->bit; + bits->bit >>= 1; + if (!bits->bit) { + if (bits->col == 12) { fprintf (bits->fp, ",\n\t"); bits->col = 0; } + else if (bits->col != 0) fprintf (bits->fp, ", "); + fprintf (bits->fp, "0x%02X", bits->byte); + bits->col++; + bits->byte_count++; + bits->bit = 0x80; + bits->byte = 0; + } +} + +void output_bitstream (struct bytepointer *bp, int max_width, int scaled) { + unsigned char *p; + struct bitoutput bits; + int col, i, v, lookup[256], offset, offsets[96]; + + qsort (bp->histogram, 256, sizeof (struct histogram), histcmp); + for (i=0; i<256; i++) { + lookup[bp->histogram[i].position] = i; + } + + bits.fp = fopen ("font.h", "w"); + bits.byte = 0; + bits.bit = 0x80; + bits.col = 0; + bits.byte_count = 0; + + offset = 0; + offsets[0] = 0; + + fprintf (bits.fp, + "/********************************************************************\n" + "* Font File format:\n" + "* 2 bits: =0, opcode lut index 0\n" + "* =1, opcode lut index is in next 3 bits + 1\n" + "* =2, opcode lut index is in next 4 bits + 9\n" + "* =3, opcode lut index is in next 6 bits + 25\n" + "********************************************************************/\n"); + fprintf (bits.fp, "unsigned char mfont[] = {\n\t"); + for (p = bp->start; p <= bp->p; p++) { + v = lookup[*p]; + if (v == 0) { + save_bits (&bits, 0); + save_bits (&bits, 0); + } else if (v < 9) { + v = v-1; + save_bits (&bits, 0); + save_bits (&bits, 1); + save_bits (&bits, v & 4); + save_bits (&bits, v & 2); + save_bits (&bits, v & 1); + } else if (v < 25) { + v = v-9; + save_bits (&bits, 1); + save_bits (&bits, 0); + save_bits (&bits, v & 8); + save_bits (&bits, v & 4); + save_bits (&bits, v & 2); + save_bits (&bits, v & 1); + } else { + v = v-25; + save_bits (&bits, 1); + save_bits (&bits, 1); + save_bits (&bits, v & 32); + save_bits (&bits, v & 16); + save_bits (&bits, v & 8); + save_bits (&bits, v & 4); + save_bits (&bits, v & 2); + save_bits (&bits, v & 1); + } + if (*p == OPCODE_EOC) { + while (bits.bit != 0x80) save_bits (&bits, 0); + offsets[++offset] = bits.byte_count; + } + + } + fprintf (bits.fp, "};\n"); + + fprintf (bits.fp, "unsigned char flut[] = {\n\t"); + for (i=0, col=0; bp->histogram[i].count; i++) { + if (col == 14) { fprintf (bits.fp, ",\n\t"); col = 0; } + else if (col != 0) fprintf (bits.fp, ", "); + fprintf (bits.fp, "%3d", bp->histogram[i].position); + col++; + bits.byte_count++; + } + fprintf (bits.fp, "};\n"); + + fprintf (bits.fp, "unsigned int foffs[] = {\n\t"); + for (i=0, col=0; iglyph; + if ( (error = FT_Render_Glyph (slot, FT_RENDER_MODE_MONO))) { + fprintf (stderr, "Error loading char"); + exit (2); + } + if ( (error = FT_Get_Glyph (slot, &glyph))) { + fprintf (stderr, "Error loading char"); + exit (2); + } + bitmap = &slot->bitmap; + if ( (int) (slot->bitmap_top - bitmap->rows) < max_decender) { + max_decender = slot->bitmap_top - bitmap->rows; + } + if (bitmap->rows > max_width) + max_width = bitmap->rows; + } + max_width++; + + /* Generate glyphs and encode */ + for (c=32; c < 127; c++) { + if ( (error = FT_Load_Char (face, c, FT_LOAD_TARGET_MONO))) { + fprintf (stderr, "Error loading char"); + exit (2); + } + slot = face->glyph; + if ( (error = FT_Render_Glyph (slot, FT_RENDER_MODE_MONO))) { + fprintf (stderr, "Error loading char"); + exit (2); + } + if ( (error = FT_Get_Glyph (slot, &glyph))) { + fprintf (stderr, "Error loading char"); + exit (2); + } + + bitmap = &slot->bitmap; + advance = slot->bitmap_left; + if (advance < 0) advance = 0; + + color = 0; + for (; advance > 0; advance--) { + save_byte (&bp, OPCODE_EOL); + if (verbose) printf ("\n"); + } + for (x=0; x < bitmap->width; x++) { + int repeat = 0; + + // Check for repeating line + if (x > 0) { + int v0; + for (y = bitmap->rows-1; y >= 0; y--) { + byte = (x-1) /8; + bit = 0x80 >> ( (x-1) &7); + v0 = (bitmap->buffer[y*bitmap->pitch + byte]&bit) ? 1: 0; + byte = x/8; + bit = 0x80 >> (x&7); + v = (bitmap->buffer[y*bitmap->pitch + byte]&bit) ? 1: 0; + if (v0 != v) + break; + } + if (y < 0) repeat = 1; + } + // Find end of spaces + for (y0=0; y0rows; y0++) { + byte = x/8; + bit = 0x80 >> (x&7); + v = (bitmap->buffer[y0*bitmap->pitch + byte]&bit) ? 1: 0; + if (v) break; + } + offset = -max_decender+ (slot->bitmap_top - bitmap->rows); + if (y0 == bitmap->rows) offset = 0; + if (verbose) { + for (y=0; yrows-1; y>=y0; y--) { + byte = x/8; + bit = 0x80 >> (x&7); + v = (bitmap->buffer[y*bitmap->pitch + byte]&bit) ? 1: 0; + if (verbose) { + if (v) + printf (repeat ? "*" : "#"); + else + printf (" "); + } + + if (v==color) offset++; + else { + if (!repeat) save_byte (&bp, offset); + color ^= 1; + offset = 1; + } + } + if (repeat) { + save_byte (&bp, OPCODE_REPEAT); + } else { + save_byte (&bp, offset); + save_byte (&bp, OPCODE_EOL); + } + color = 0; + if (verbose) printf ("\n"); + } + advance = (slot->advance.x >> 6) - bitmap->width - + ( (slot->bitmap_left > 0) ? slot->bitmap_left : 0); + if (advance < 0) advance = 0; + for (; advance > 0; advance--) { + save_byte (&bp, OPCODE_EOL); + if (verbose) printf ("\n"); + } + save_byte (&bp, OPCODE_EOC); + FT_Done_Glyph (glyph); + } + output_bitstream (&bp, max_width, scaled); +} diff --git a/output.vaw b/output.vaw new file mode 100644 index 0000000..b35219a Binary files /dev/null and b/output.vaw differ diff --git a/screen.png b/screen.png new file mode 100644 index 0000000..d5c29b1 Binary files /dev/null and b/screen.png differ