Subversion Repositories group.electronics

Rev

Rev 186 | Blame | Compare with Previous | Last modification | View Log | RSS feed

using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace nitdcscore {
    public class Oled {
        public const int LCD_WIDTH = 128;
        public const int LCD_HEIGHT = 64;

        public const int EXTERNALVCC = 0x01;
        public const int SWITCHCAPVCC = 0x02;
        public const int SETLOWCOLUMN = 0x00;
        public const int SETHIGHCOLUMN = 0x10;
        public const int MEMORYMODE = 0x20;
        public const int COLUMNADDR = 0x21;
        public const int PAGEADDR = 0x22;
        public const int DEACTIVATESCROLL = 0x2E;
        public const int SETSTARTLINE = 0x40;

        public const int SETCONTRAST = 0x81;
        public const int CHARGEPUMP = 0x8D;

        public const int SEGREMAP = 0xA0;
        public const int DISPLAYALLON_RESUME = 0xA4;
        public const int DISPLAYALLON = 0xA5;
        public const int NORMALDISPLAY = 0xA6;
        public const int INVERTDISPLAY = 0xA7;
        public const int SETMULTIPLEX = 0xA8;
        public const int DISPLAYOFF = 0xAE;
        public const int DISPLAYON = 0xAF;

        public const int COMSCANINC = 0xC0;
        public const int COMSCANDEC = 0xC8;

        public const int SETDISPLAYOFFSET = 0xD3;
        public const int SETCOMPINS = 0xDA;
        public const int SETVCOMDETECT = 0xDB;
        public const int SETDISPLAYCLOCKDIV = 0xD5;
        public const int SETPRECHARGE = 0xD9;

        public byte[] buffer = new byte[Oled.LCD_WIDTH / 8 * Oled.LCD_HEIGHT];

        public int vccstate { get; set; }
        public byte i2caddr { get; set; }

        public i2cmaster i2c { get; set; }

        public Oled(int vccstate, byte i2caddr, i2cmaster i2c) {
            this.vccstate = vccstate;
            this.i2caddr = i2caddr;
            this.i2c = i2c;

            
            for (int t = 0; t < (LCD_WIDTH * LCD_HEIGHT / 8); t++) {
                buffer[t] = new byte();
                buffer[t] = 0x00;
            }


        }

        public void init() {
            // Init sequence

            oled_command(DISPLAYOFF);
            oled_command(SETDISPLAYCLOCKDIV);
            oled_command(0x80); // Suggested ratio

            oled_command(SETMULTIPLEX);
            oled_command(LCD_HEIGHT - 1);

            oled_command(SETDISPLAYOFFSET);
            oled_command(0x00); // No offset
            oled_command(SETSTARTLINE | 0x00); // Start line 0
            oled_command(CHARGEPUMP);
            if (this.vccstate == EXTERNALVCC)
                oled_command(0x10);
            else
                oled_command(0x14);

            oled_command(MEMORYMODE);
            oled_command(0x00);
            oled_command(SEGREMAP | 0x01);
            oled_command(COMSCANDEC);

            // 128-64 specific
            oled_command(SETCOMPINS);
            oled_command(0x12);
            oled_command(SETCONTRAST);
            if (this.vccstate == EXTERNALVCC)
                oled_command(0x9F);
            else
                oled_command(0xCF);

            oled_command(SETPRECHARGE);
            if (this.vccstate == EXTERNALVCC)
                oled_command(0x22);
            else
                oled_command(0xF1);

            oled_command(SETVCOMDETECT);
            oled_command(0x40);
            oled_command(DISPLAYALLON_RESUME);
            oled_command(NORMALDISPLAY);
            oled_command(DEACTIVATESCROLL);

            oled_command(DISPLAYON);

        }

        public void display() {
            oled_command(COLUMNADDR);           
            oled_command(0x00);         // Column start address
            oled_command(LCD_WIDTH - 1);  // Column end address
            


            oled_command(PAGEADDR);
            oled_command(0x00); // Page start address
            oled_command(7);    // Page end address (7 for 64 pixels)

            // 1024 -  388ms
            //   16 - 1190ms

            const int BYTES_TO_SEND = 1024;

            for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++) {
                byte[] data = new byte[BYTES_TO_SEND + 1];
                data[0] = 0x40;
                for (int j = 1; j < BYTES_TO_SEND + 1; j++) {
                    data[j] = buffer[i];
                    i++;
                }
                i--;
                i2c.WriteI2cData(i2cUtils.addressToWrite(this.i2caddr), data, BYTES_TO_SEND + 1);
            }
            
        }

        public int oled_command(byte c) {
            byte[] data = { 0x00, c };

            i2c.SetGpio(0, 1);
            i2c.WriteI2cData(i2cUtils.addressToWrite(this.i2caddr), data, 2);
            i2c.SetGpio(0, 0);
            return 0;
        }
    }

    public class BmpImage {
        public string filename;     // The image filename
        public int filesize;        // Total size of the 
        public int datastart;       // Location of the start of image data
        public int imagesize;       // The size of actual image data
        public int width;           // Width of the image
        public int height;          // Height of the image
        public int bpp;             // Bits per pixel of the image
        public int bytepad;         // Bytes padded to align image row
        public byte[] data;         // The bmp image data
        public BitArray[] bitdata;  // Data in bitarray format

        public BmpImage(string filename) {
            this.filename = filename;
            filesize = 0;
            datastart = 0xffffff;
            imagesize = 0;
            width = 0;
            height = 0;
            bpp = 0;
            bytepad = 0;
            data = new byte[8192];
            bitdata = new BitArray[1];

            this.load();
        }

        public int load() {
            using (BinaryReader b = new BinaryReader(File.Open(this.filename, FileMode.Open))) {
                int pos = 0;

                int readLen = (int)b.BaseStream.Length;
                while (pos < readLen) {
                    // Read the image data from the file
                    if (pos >= this.datastart) {

                        // BMP format stored in 'data'
                        int imgptr = 0;
                        while (imgptr < this.imagesize) {
                            this.data[imgptr++] = b.ReadByte();
                        }
                        //Console.WriteLine("Read " + imgptr.ToString() + " bytes");

                        // Raw decoded data stored in 'bitdata'
                        this.bitdata = new BitArray[this.height];
                        int x = 0;      // Cursor X position
                        int y = 0;      // Cursor Y position
                        int i = 0;    // Current byte position
                        //Console.WriteLine("W:{0} H:{1}", img.width.ToString(), img.height.ToString());

                        for (y = this.height - 1; y >= 0; y--) {

                            this.bitdata[y] = new BitArray(this.width);
                            x = 0;
                            while (x < this.width) {
                                if (x >= this.width)
                                    break;
                                // Reverse the bit cause its stored back to front in bmp format
                                byte br;
                                br = this.data[i];
                                br = (byte)((br * 0x0202020202 & 0x010884422010) % 1023);

                                for (int bitidx = 0; bitidx < 8; bitidx++) {
                                    int bitpos = x + bitidx;
                                    if (bitpos >= this.width)
                                        break;
                                    int val = br >> bitidx & 0x01;
                                    //Console.WriteLine("X:{0} Y:{1}", x.ToString(), y.ToString());
                                    this.bitdata[y].Set(bitpos, Convert.ToBoolean(val));
                                }
                                x += 8;
                                i++;
                            }
                            i += this.bytepad; // Account for the padding
                        }
                        break;
                    }

                    // Read the header data from the file
                    switch (pos) {
                        // Magic number header ("BM")
                        case 0x00: {
                                byte t1 = b.ReadByte();
                                byte t2 = b.ReadByte();
                                pos += 2;
                                string text = ((char)t1).ToString();
                                text += ((char)t2).ToString();
                            }
                            break;
                        // The size of the image file
                        case 0x02: {
                                this.filesize = (int)b.ReadUInt32();
                                pos += 4;
                                if (this.filesize != b.BaseStream.Length)
                                    Console.WriteLine("Size mismatch: " + this.filesize.ToString() + ":" + b.BaseStream.Length.ToString());
                                else
                                    Console.WriteLine("Size: " + this.filesize.ToString());
                            }
                            break;
                        // The start of image data location
                        case 0x0a: {
                                this.datastart = (int)b.ReadUInt32();
                                pos += 4;
                            }
                            break;
                        // The DIB header file size
                        case 0x0e: {
                                //UInt32 dibhs = b.ReadUInt32();
                                b.ReadUInt32();
                                pos += 4;
                            }
                            break;
                        // Bitmap width in pixels
                        case 0x12: {
                                this.width = (int)b.ReadInt32();
                                pos += 4;

                                // Rows aligned to a DWORD
                                int bitsOver = this.width % 32;
                                if (bitsOver > 0) {
                                    this.bytepad = (32 - bitsOver) / 8;
                                }
                            }
                            break;
                        // Bitmap height in pixels
                        case 0x16: {
                                this.height = (int)b.ReadInt32();
                                pos += 4;
                            }
                            break;
                        // Bitmap bytes per pixel
                        case 0x1c: {
                                this.bpp = (int)b.ReadUInt16();
                                pos += 2;
                            }
                            break;
                        // Bitmap size
                        case 0x22: {
                                this.imagesize = (int)b.ReadUInt32();
                                pos += 4;
                            }
                            break;

                        // Not an important piece of data; dump it
                        default:
                            b.ReadByte();
                            pos++;
                            break;
                    }
                }
            }
            return 0;
        }

    }

    public class BitImage {

        public BitArray[] bitdata;
        public int width;
        public int height;

        public BitImage(int width, int height) {
            this.width = width;
            this.height = height;
            this.bitdata = new BitArray[this.height];

            for (int i = 0; i < this.height; i++) {
                this.bitdata[i] = new BitArray(this.width);
            }
        }

        public BitImage(BitArray[] matrix) {
            this.width = matrix[0].Length;
            this.height = matrix.Length;
            this.bitdata = BitImage.copy(matrix);
        }

        public BitImage(BmpImage bmp) {
            this.width = bmp.width;
            this.height = bmp.height;
            this.bitdata = BitImage.copy(bmp.bitdata);
        }

        public BitImage(BitImage bit) {
            this.width = bit.width;
            this.height = bit.height;
            this.bitdata = BitImage.copy(bit.bitdata);
        }

        public BitImage(Bitmap bmp) {
            //Bitmap bmp = new Bitmap(nitdcscore.Properties.Resources.flap_bg);
            //BitImage bi = new BitImage(new Bitmap(Resources.flap_needle));
            this.width = bmp.Width;
            this.height = bmp.Height;
            this.bitdata = new BitArray[this.height];

            for (int y = 0; y < this.height; y++) {
                this.bitdata[y] = new BitArray(this.width);
                for (int x = 0; x < this.width; x++) {
                    Color c = bmp.GetPixel(x, y);
                    
                    if (c.GetBrightness() == 0)
                        this.bitdata[y].Set(x, false);
                    else
                        this.bitdata[y].Set(x, true);
                }
            }
        }



        public void fillAll(bool color) {
            for (int i = 0; i < this.height; i++) {
                this.bitdata[i].SetAll(color);
            }
        }

        public void drawPixel(int x, int y, bool color) {
            if (x < 0 || y < 0 || x >= this.width || y >= this.height)
                return;
            this.bitdata[y].Set(x, color);
        }

        public void drawVLine(int x, int y, int h, bool color) {

            if (x < 0 || x >= this.width)
                return;

            if (y < 0) {
                h += y;
                y = 0;
            }

            if ((y + h) > this.height)
                h = this.height - y;

            if (h <= 0)
                return;

            for (int i = y; i < y + h; i++) {
                this.bitdata[i].Set(x, color);
            }
        }

        public void drawHLine(int x, int y, int w, bool color) {
            if (y < 0 || y >= this.height)
                return;
            if (x < 0) {
                w += x;
                x = 0;
            }

            if ((x + w) > this.width)
                w = this.width - x;

            if (w <= 0)
                return;

            for (int i = x; i < x + height; i++) {
                this.bitdata[y].Set(i, color);
            }
        }


        public void drawLine(int x0, int y0, int x1, int y1, bool color) {
            bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
            if (steep) {
                swap(ref x0, ref y0);
                swap(ref x1, ref y1);
            }

            if (x0 > x1) {
                swap(ref x0, ref x1);
                swap(ref y0, ref y1);
            }

            int dx, dy;
            dx = x1 - x0;
            dy = Math.Abs(y1 - y0);

            int err = dx / 2;
            int ystep;

            if (y0 < y1)
                ystep = 1;
            else
                ystep = -1;

            for (; x0 <= x1; x0++) {
                if (steep)
                    drawPixel(y0, x0, color);
                else
                    drawPixel(x0, y0, color);

                err -= dy;
                if (err < 0) {
                    y0 += ystep;
                    err += dx;
                }
            }
        }

        public void drawRect(int x, int y, int w, int h, bool color) {
            drawHLine(x, y, w, color);
            drawHLine(x, y + h - 1, w, color);
            drawVLine(x, y, h, color);
            drawVLine(x + w - 1, y, h, color);
        }

        public void fillRect(int x, int y, int w, int h, bool color) {
            for (int i = x; i < x + w; i++) {
                drawVLine(i, y, h, color);
            }
        }


        public void drawCircle(int x0, int y0, int r, bool color) {
            int f = 1 - r;
            int ddF_x = 1;
            int ddF_y = -2 * r;
            int x = 0;
            int y = r;

            drawPixel(x0, y0 + r, color);
            drawPixel(x0, y0 - r, color);
            drawPixel(x0 + r, y0, color);
            drawPixel(x0 - r, y0, color);

            while (x < y) {
                if (f >= 0) {
                    y--;
                    ddF_y += 2;
                    f += ddF_y;
                }
                x++;
                ddF_x += 2;
                f += ddF_x;

                drawPixel(x0 + x, y0 + y, color);
                drawPixel(x0 - x, y0 + y, color);
                drawPixel(x0 + x, y0 - y, color);
                drawPixel(x0 - x, y0 - y, color);
                drawPixel(x0 + y, y0 + x, color);
                drawPixel(x0 - y, y0 + x, color);
                drawPixel(x0 + y, y0 - x, color);
                drawPixel(x0 - y, y0 - x, color);
            }
        }

        public void fillCircle(int x0, int y0, int r, bool color) {
            drawVLine(x0, y0 - r, 2 * r + 1, color);
            fillCircleHelper(x0, y0, r, 3, 0, color);
        }

        public void fillCircleHelper(int x0, int y0, int r, int corner, int delta, bool color) {
            int f = 1 - r;
            int ddF_x = 1;
            int ddF_y = -2 * r;
            int x = 0;
            int y = r;

            while (x < y) {
                if (f >= 0) {
                    y--;
                    ddF_y += 2;
                    f += ddF_y;
                }
                x++;
                ddF_x += 2;
                f += ddF_x;

                if ((corner & 0x01) > 0) {
                    drawVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
                    drawVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
                }

                if ((corner & 0x02) > 0) {
                    drawVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
                    drawVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
                }
            }
        }

        public void drawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, bool color) {
            drawLine(x0, y0, x1, y1, color);
            drawLine(x1, y1, x2, y2, color);
            drawLine(x2, y2, x0, y0, color);
        }

        public void drawPoly(Point[] points, bool color) {
            int idx = 1;
            int c = points.Length;
            while (idx < c) {
                drawLine(points[idx - 1].X, points[idx - 1].Y, points[idx].X, points[idx].Y, color);
                idx++;
            }
            drawLine(points[c - 1].X, points[c - 1].Y, points[0].X, points[0].Y, color);
        }

        public void drawChar(int x, int y, byte c, bool color, int size) {
            for (int i = 0; i < 6; i++) {
                byte line;
                if (i < 5)
                    line = Font.classic[c * 5 + i];
                else
                    line = 0x00;
                for (int j = 0; j < 8; j++, line >>= 1) {
                    if ((line & 0x01) > 0) {
                        if (size == 1)
                            drawPixel(x + i, y + j, color);
                        else
                            fillRect(x + (i * size), y + (j * size), size, size, color);
                    }
                    /*
                    } else {
                        if (size == 1)
                            drawPixel(x + i, y + j, !color);
                        else
                            fillRect(x+i*size, y+j*size, size, size, !color);
                    }
                    */
                }

            }
        }

        public void write(string text, int x, int y, bool color, int size) {
            Point cur = new Point(x, y);
            foreach (byte c in text) {
                if (c == '\n') {
                    cur.Y += size * 8;
                    cur.X = x;
                } else if (c == '\r') {

                } else if (c == ' ') {
                    // Quicker then calling drawChar function
                    cur.X += size * 6;
                } else {
                    drawChar(cur.X, cur.Y, c, color, size);
                    cur.X += size * 6;
                }
            }
        }

        public static void swap(ref int i, ref int j) {
            int t = i; i = j; j = t;
        }


        /// <summary>
        /// Copies the provided image onto the objects image, at position specified
        /// by the ox and oy parms. Only copies lit bits (not dark)
        /// </summary>
        /// <param name="bit">The bit image to paste onto this image</param>
        /// <param name="ox">The X origin</param>
        /// <param name="oy">The Y origin</param>
        public void blit(BitImage bit, int ox, int oy) {
            int x = 0;
            int y = 0;

            for (y = 0; y < bit.height; y++) {
                for (x = 0; x < bit.width; x++) {
                    if (bit.bitdata[y].Get(x)) {
                        if (oy + y > this.height -1  || ox + x > this.width)
                            return;
                        this.bitdata[oy + y].Set(ox + x, true);
                    }
                }
            }
        }

        /// <summary>
        /// Rotate the image by the specified degrees.
        /// </summary>
        /// <param name="degrees">Degrees to rotate</param>
        public void rotate(int degrees) {
            this.bitdata = (BitArray[])BitImage.rotateMatrix(this.bitdata, degrees).bitdata;
            this.width = this.bitdata[0].Length;
            this.height = this.bitdata.Length;
        }

        public byte[] toByteArray() {
            return BitImage.toByteArray(this.bitdata);
        }

        public static BitArray[] copy(BitArray[] ba) {
            int width = ba[0].Length;
            int height = ba.Length;

            BitArray[] newba = new BitArray[height];

            for (int y = 0; y < height; y++) {
                newba[y] = new BitArray(width);
                for (int x = 0; x < width; x++) {
                    if (ba[y].Get(x)) {
                        newba[y].Set(x, true);
                    }
                }
            }
            return newba;
        }

        public static byte[] toByteArray(BitArray[] ba) {
            int width = ba[0].Length;
            int height = ba.Length;

            int size = width * height / 8;
            byte[] buffer = new byte[size];
            if (size % 8 != 0) {
                Console.WriteLine("toByteArray only supports mod 8 array (size: {0}, misalinged by {1})", size, size % 8);
                return null;
            }

            BitArray data = new BitArray(width * height);
            int i = 0;
            for (int y = 0; y < ba.Length; y += 8) {
                for (int x = 0; x < width; x++) {
                    for (int j = 0; j < 8; j++) {
                        data.Set(i, ba[y+j].Get(x));
                        i++;
                    }
                }
            }
            data.CopyTo(buffer, 0);
            return buffer;
        }

        /// <summary>
        /// Rotates a matrix by provided degrees. Note that the size of the
        ///  image may change with this process
        /// </summary>
        /// <returns>A matrix with the rotated elements</returns>
        /// <param name="matrix">The BitArray matrix to be rotated</param>
        /// <param name="degrees">The degress to rotate the matrix by</param>
        public static BitImage rotateMatrix(BitArray[] matrix, int degrees) {
            // Widths of the matrix
            int oldwidth = matrix[0].Length;
            int oldheight = matrix.Length;

            // If no rotation nessecary, just return a new BitImage
            if (degrees == 0 || degrees == 360) {
                BitImage bit = new BitImage(oldwidth, oldheight);
                bit.bitdata = (BitArray[])matrix;
                return bit;
            }

            // Calc the new image meta data
            double rads = degrees * Math.PI / 180;
            double tcos = Math.Cos(rads);
            double tsin = Math.Sin(rads);

            Point p1 = new Point((int)(-oldheight * tsin), (int)(oldheight * tcos));
            Point p2 = new Point((int)(oldwidth * tcos - oldheight * tsin),
                (int)(oldheight * tcos + oldwidth * tsin));
            Point p3 = new Point((int)(oldwidth * tcos), (int)(oldwidth * tsin));
            Point min = new Point(Math.Min(0, Math.Min(p1.X, Math.Min(p2.X, p3.X))),
                Math.Min(0, Math.Min(p1.Y, Math.Min(p2.Y, p3.Y))));
            Point max = new Point(Math.Max(p1.X, Math.Max(p2.X, p3.X)),
                Math.Max(p1.Y, Math.Max(p2.Y, p3.Y)));
            Point size = new Point(max.X - min.X, max.Y - min.Y);

            // Prepopulate the bitarray
            BitArray[] bitmatrix = new BitArray[size.Y];
            for (int y = 0; y < size.Y; y++) {
                bitmatrix[y] = new BitArray(size.X);
            }

            // Transform pixel by pixel
            for (int y = 0; y < size.Y - 1; y++) {
                for (int x = 0; x < size.X - 1; x++) {
                    int sourcex = (int)((x + min.X) * tcos + (y + min.Y) * tsin);
                    int sourcey = (int)((y + min.Y) * tcos - (x + min.X) * tsin);

                    if (sourcex < 0 || sourcex > oldwidth - 1 || sourcey < 0 || sourcey > oldheight - 1)
                        continue;
                    if (x < 0 || x > size.X - 1 || y < 0 || y > size.Y - 1)
                        continue;

                    bitmatrix[y].Set(x, matrix[sourcey].Get(sourcex));
                }
            }

            BitImage bi = new BitImage(bitmatrix[0].Length, bitmatrix.Length);
            bi.bitdata = (BitArray[])bitmatrix;
            return bi;
        }

    }

    public struct Point {
        private int x;
        private int y;

        public int X {
            get {
                return this.x;
            }
            set {
                this.x = value;
            }
        }

        public int Y {
            get {
                return this.y;
            }
            set {
                this.y = value;
            }
        }

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public class Font {
        public Font() {
        }

        public static byte[] classic = new byte[] {
            0x00, 0x00, 0x00, 0x00, 0x00,
            0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
            0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
            0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
            0x18, 0x3C, 0x7E, 0x3C, 0x18,
            0x1C, 0x57, 0x7D, 0x57, 0x1C,
            0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
            0x00, 0x18, 0x3C, 0x18, 0x00,
            0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
            0x00, 0x18, 0x24, 0x18, 0x00,
            0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
            0x30, 0x48, 0x3A, 0x06, 0x0E,
            0x26, 0x29, 0x79, 0x29, 0x26,
            0x40, 0x7F, 0x05, 0x05, 0x07,
            0x40, 0x7F, 0x05, 0x25, 0x3F,
            0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
            0x7F, 0x3E, 0x1C, 0x1C, 0x08,
            0x08, 0x1C, 0x1C, 0x3E, 0x7F,
            0x14, 0x22, 0x7F, 0x22, 0x14,
            0x5F, 0x5F, 0x00, 0x5F, 0x5F,
            0x06, 0x09, 0x7F, 0x01, 0x7F,
            0x00, 0x66, 0x89, 0x95, 0x6A,
            0x60, 0x60, 0x60, 0x60, 0x60,
            0x94, 0xA2, 0xFF, 0xA2, 0x94,
            0x08, 0x04, 0x7E, 0x04, 0x08,
            0x10, 0x20, 0x7E, 0x20, 0x10,
            0x08, 0x08, 0x2A, 0x1C, 0x08,
            0x08, 0x1C, 0x2A, 0x08, 0x08,
            0x1E, 0x10, 0x10, 0x10, 0x10,
            0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
            0x30, 0x38, 0x3E, 0x38, 0x30,
            0x06, 0x0E, 0x3E, 0x0E, 0x06,
            0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x5F, 0x00, 0x00,
            0x00, 0x07, 0x00, 0x07, 0x00,
            0x14, 0x7F, 0x14, 0x7F, 0x14,
            0x24, 0x2A, 0x7F, 0x2A, 0x12,
            0x23, 0x13, 0x08, 0x64, 0x62,
            0x36, 0x49, 0x56, 0x20, 0x50,
            0x00, 0x08, 0x07, 0x03, 0x00,
            0x00, 0x1C, 0x22, 0x41, 0x00,
            0x00, 0x41, 0x22, 0x1C, 0x00,
            0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
            0x08, 0x08, 0x3E, 0x08, 0x08,
            0x00, 0x80, 0x70, 0x30, 0x00,
            0x08, 0x08, 0x08, 0x08, 0x08,
            0x00, 0x00, 0x60, 0x60, 0x00,
            0x20, 0x10, 0x08, 0x04, 0x02,
            0x3E, 0x51, 0x49, 0x45, 0x3E,
            0x00, 0x42, 0x7F, 0x40, 0x00,
            0x72, 0x49, 0x49, 0x49, 0x46,
            0x21, 0x41, 0x49, 0x4D, 0x33,
            0x18, 0x14, 0x12, 0x7F, 0x10,
            0x27, 0x45, 0x45, 0x45, 0x39,
            0x3C, 0x4A, 0x49, 0x49, 0x31,
            0x41, 0x21, 0x11, 0x09, 0x07,
            0x36, 0x49, 0x49, 0x49, 0x36,
            0x46, 0x49, 0x49, 0x29, 0x1E,
            0x00, 0x00, 0x14, 0x00, 0x00,
            0x00, 0x40, 0x34, 0x00, 0x00,
            0x00, 0x08, 0x14, 0x22, 0x41,
            0x14, 0x14, 0x14, 0x14, 0x14,
            0x00, 0x41, 0x22, 0x14, 0x08,
            0x02, 0x01, 0x59, 0x09, 0x06,
            0x3E, 0x41, 0x5D, 0x59, 0x4E,
            0x7C, 0x12, 0x11, 0x12, 0x7C,
            0x7F, 0x49, 0x49, 0x49, 0x36,
            0x3E, 0x41, 0x41, 0x41, 0x22,
            0x7F, 0x41, 0x41, 0x41, 0x3E,
            0x7F, 0x49, 0x49, 0x49, 0x41,
            0x7F, 0x09, 0x09, 0x09, 0x01,
            0x3E, 0x41, 0x41, 0x51, 0x73,
            0x7F, 0x08, 0x08, 0x08, 0x7F,
            0x00, 0x41, 0x7F, 0x41, 0x00,
            0x20, 0x40, 0x41, 0x3F, 0x01,
            0x7F, 0x08, 0x14, 0x22, 0x41,
            0x7F, 0x40, 0x40, 0x40, 0x40,
            0x7F, 0x02, 0x1C, 0x02, 0x7F,
            0x7F, 0x04, 0x08, 0x10, 0x7F,
            0x3E, 0x41, 0x41, 0x41, 0x3E,
            0x7F, 0x09, 0x09, 0x09, 0x06,
            0x3E, 0x41, 0x51, 0x21, 0x5E,
            0x7F, 0x09, 0x19, 0x29, 0x46,
            0x26, 0x49, 0x49, 0x49, 0x32,
            0x03, 0x01, 0x7F, 0x01, 0x03,
            0x3F, 0x40, 0x40, 0x40, 0x3F,
            0x1F, 0x20, 0x40, 0x20, 0x1F,
            0x3F, 0x40, 0x38, 0x40, 0x3F,
            0x63, 0x14, 0x08, 0x14, 0x63,
            0x03, 0x04, 0x78, 0x04, 0x03,
            0x61, 0x59, 0x49, 0x4D, 0x43,
            0x00, 0x7F, 0x41, 0x41, 0x41,
            0x02, 0x04, 0x08, 0x10, 0x20,
            0x00, 0x41, 0x41, 0x41, 0x7F,
            0x04, 0x02, 0x01, 0x02, 0x04,
            0x40, 0x40, 0x40, 0x40, 0x40,
            0x00, 0x03, 0x07, 0x08, 0x00,
            0x20, 0x54, 0x54, 0x78, 0x40,
            0x7F, 0x28, 0x44, 0x44, 0x38,
            0x38, 0x44, 0x44, 0x44, 0x28,
            0x38, 0x44, 0x44, 0x28, 0x7F,
            0x38, 0x54, 0x54, 0x54, 0x18,
            0x00, 0x08, 0x7E, 0x09, 0x02,
            0x18, 0xA4, 0xA4, 0x9C, 0x78,
            0x7F, 0x08, 0x04, 0x04, 0x78,
            0x00, 0x44, 0x7D, 0x40, 0x00,
            0x20, 0x40, 0x40, 0x3D, 0x00,
            0x7F, 0x10, 0x28, 0x44, 0x00,
            0x00, 0x41, 0x7F, 0x40, 0x00,
            0x7C, 0x04, 0x78, 0x04, 0x78,
            0x7C, 0x08, 0x04, 0x04, 0x78,
            0x38, 0x44, 0x44, 0x44, 0x38,
            0xFC, 0x18, 0x24, 0x24, 0x18,
            0x18, 0x24, 0x24, 0x18, 0xFC,
            0x7C, 0x08, 0x04, 0x04, 0x08,
            0x48, 0x54, 0x54, 0x54, 0x24,
            0x04, 0x04, 0x3F, 0x44, 0x24,
            0x3C, 0x40, 0x40, 0x20, 0x7C,
            0x1C, 0x20, 0x40, 0x20, 0x1C,
            0x3C, 0x40, 0x30, 0x40, 0x3C,
            0x44, 0x28, 0x10, 0x28, 0x44,
            0x4C, 0x90, 0x90, 0x90, 0x7C,
            0x44, 0x64, 0x54, 0x4C, 0x44,
            0x00, 0x08, 0x36, 0x41, 0x00,
            0x00, 0x00, 0x77, 0x00, 0x00,
            0x00, 0x41, 0x36, 0x08, 0x00,
            0x02, 0x01, 0x02, 0x04, 0x02,
            0x3C, 0x26, 0x23, 0x26, 0x3C,
            0x1E, 0xA1, 0xA1, 0x61, 0x12,
            0x3A, 0x40, 0x40, 0x20, 0x7A,
            0x38, 0x54, 0x54, 0x55, 0x59,
            0x21, 0x55, 0x55, 0x79, 0x41,
            0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
            0x21, 0x55, 0x54, 0x78, 0x40,
            0x20, 0x54, 0x55, 0x79, 0x40,
            0x0C, 0x1E, 0x52, 0x72, 0x12,
            0x39, 0x55, 0x55, 0x55, 0x59,
            0x39, 0x54, 0x54, 0x54, 0x59,
            0x39, 0x55, 0x54, 0x54, 0x58,
            0x00, 0x00, 0x45, 0x7C, 0x41,
            0x00, 0x02, 0x45, 0x7D, 0x42,
            0x00, 0x01, 0x45, 0x7C, 0x40,
            0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
            0xF0, 0x28, 0x25, 0x28, 0xF0,
            0x7C, 0x54, 0x55, 0x45, 0x00,
            0x20, 0x54, 0x54, 0x7C, 0x54,
            0x7C, 0x0A, 0x09, 0x7F, 0x49,
            0x32, 0x49, 0x49, 0x49, 0x32,
            0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
            0x32, 0x4A, 0x48, 0x48, 0x30,
            0x3A, 0x41, 0x41, 0x21, 0x7A,
            0x3A, 0x42, 0x40, 0x20, 0x78,
            0x00, 0x9D, 0xA0, 0xA0, 0x7D,
            0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
            0x3D, 0x40, 0x40, 0x40, 0x3D,
            0x3C, 0x24, 0xFF, 0x24, 0x24,
            0x48, 0x7E, 0x49, 0x43, 0x66,
            0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
            0xFF, 0x09, 0x29, 0xF6, 0x20,
            0xC0, 0x88, 0x7E, 0x09, 0x03,
            0x20, 0x54, 0x54, 0x79, 0x41,
            0x00, 0x00, 0x44, 0x7D, 0x41,
            0x30, 0x48, 0x48, 0x4A, 0x32,
            0x38, 0x40, 0x40, 0x22, 0x7A,
            0x00, 0x7A, 0x0A, 0x0A, 0x72,
            0x7D, 0x0D, 0x19, 0x31, 0x7D,
            0x26, 0x29, 0x29, 0x2F, 0x28,
            0x26, 0x29, 0x29, 0x29, 0x26,
            0x30, 0x48, 0x4D, 0x40, 0x20,
            0x38, 0x08, 0x08, 0x08, 0x08,
            0x08, 0x08, 0x08, 0x08, 0x38,
            0x2F, 0x10, 0xC8, 0xAC, 0xBA,
            0x2F, 0x10, 0x28, 0x34, 0xFA,
            0x00, 0x00, 0x7B, 0x00, 0x00,
            0x08, 0x14, 0x2A, 0x14, 0x22,
            0x22, 0x14, 0x2A, 0x14, 0x08,
            0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code
            0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
            0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
            0x00, 0x00, 0x00, 0xFF, 0x00,
            0x10, 0x10, 0x10, 0xFF, 0x00,
            0x14, 0x14, 0x14, 0xFF, 0x00,
            0x10, 0x10, 0xFF, 0x00, 0xFF,
            0x10, 0x10, 0xF0, 0x10, 0xF0,
            0x14, 0x14, 0x14, 0xFC, 0x00,
            0x14, 0x14, 0xF7, 0x00, 0xFF,
            0x00, 0x00, 0xFF, 0x00, 0xFF,
            0x14, 0x14, 0xF4, 0x04, 0xFC,
            0x14, 0x14, 0x17, 0x10, 0x1F,
            0x10, 0x10, 0x1F, 0x10, 0x1F,
            0x14, 0x14, 0x14, 0x1F, 0x00,
            0x10, 0x10, 0x10, 0xF0, 0x00,
            0x00, 0x00, 0x00, 0x1F, 0x10,
            0x10, 0x10, 0x10, 0x1F, 0x10,
            0x10, 0x10, 0x10, 0xF0, 0x10,
            0x00, 0x00, 0x00, 0xFF, 0x10,
            0x10, 0x10, 0x10, 0x10, 0x10,
            0x10, 0x10, 0x10, 0xFF, 0x10,
            0x00, 0x00, 0x00, 0xFF, 0x14,
            0x00, 0x00, 0xFF, 0x00, 0xFF,
            0x00, 0x00, 0x1F, 0x10, 0x17,
            0x00, 0x00, 0xFC, 0x04, 0xF4,
            0x14, 0x14, 0x17, 0x10, 0x17,
            0x14, 0x14, 0xF4, 0x04, 0xF4,
            0x00, 0x00, 0xFF, 0x00, 0xF7,
            0x14, 0x14, 0x14, 0x14, 0x14,
            0x14, 0x14, 0xF7, 0x00, 0xF7,
            0x14, 0x14, 0x14, 0x17, 0x14,
            0x10, 0x10, 0x1F, 0x10, 0x1F,
            0x14, 0x14, 0x14, 0xF4, 0x14,
            0x10, 0x10, 0xF0, 0x10, 0xF0,
            0x00, 0x00, 0x1F, 0x10, 0x1F,
            0x00, 0x00, 0x00, 0x1F, 0x14,
            0x00, 0x00, 0x00, 0xFC, 0x14,
            0x00, 0x00, 0xF0, 0x10, 0xF0,
            0x10, 0x10, 0xFF, 0x10, 0xFF,
            0x14, 0x14, 0x14, 0xFF, 0x14,
            0x10, 0x10, 0x10, 0x1F, 0x00,
            0x00, 0x00, 0x00, 0xF0, 0x10,
            0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
            0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
            0xFF, 0xFF, 0xFF, 0x00, 0x00,
            0x00, 0x00, 0x00, 0xFF, 0xFF,
            0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
            0x38, 0x44, 0x44, 0x38, 0x44,
            0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
            0x7E, 0x02, 0x02, 0x06, 0x06,
            0x02, 0x7E, 0x02, 0x7E, 0x02,
            0x63, 0x55, 0x49, 0x41, 0x63,
            0x38, 0x44, 0x44, 0x3C, 0x04,
            0x40, 0x7E, 0x20, 0x1E, 0x20,
            0x06, 0x02, 0x7E, 0x02, 0x02,
            0x99, 0xA5, 0xE7, 0xA5, 0x99,
            0x1C, 0x2A, 0x49, 0x2A, 0x1C,
            0x4C, 0x72, 0x01, 0x72, 0x4C,
            0x30, 0x4A, 0x4D, 0x4D, 0x30,
            0x30, 0x48, 0x78, 0x48, 0x30,
            0xBC, 0x62, 0x5A, 0x46, 0x3D,
            0x3E, 0x49, 0x49, 0x49, 0x00,
            0x7E, 0x01, 0x01, 0x01, 0x7E,
            0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
            0x44, 0x44, 0x5F, 0x44, 0x44,
            0x40, 0x51, 0x4A, 0x44, 0x40,
            0x40, 0x44, 0x4A, 0x51, 0x40,
            0x00, 0x00, 0xFF, 0x01, 0x03,
            0xE0, 0x80, 0xFF, 0x00, 0x00,
            0x08, 0x08, 0x6B, 0x6B, 0x08,
            0x36, 0x12, 0x36, 0x24, 0x36,
            0x06, 0x0F, 0x09, 0x0F, 0x06,
            0x00, 0x00, 0x18, 0x18, 0x00,
            0x00, 0x00, 0x10, 0x10, 0x00,
            0x30, 0x40, 0xFF, 0x01, 0x01,
            0x00, 0x1F, 0x01, 0x01, 0x1E,
            0x00, 0x19, 0x1D, 0x17, 0x12,
            0x00, 0x3C, 0x3C, 0x3C, 0x3C,
            0x00, 0x00, 0x00, 0x00, 0x00  // #255 NBSP
        };
    }
}