Shop OBEX P1 Docs P2 Docs Learn Events
Font Editor (preview #5) — Parallax Forums

Font Editor (preview #5)

RaymanRayman Posts: 14,652
edited 2015-01-23 18:39 in Propeller 1
I'm working on this font editor. It's mostly for LCD screens and LED panels.
But, I've added some options to import the font from "VGA_HiRes_Text.spin".
You can then export it and paste it back in. Run the stock "VGA_HiRes_Text_Demo" to see your font on a VGA screen.
Fonts are saved as ".rfd" files.

This program is self-contained, so just extract it anywhere and run it:

RaysFontEditor_24Aug12.zip 'Updated version 0.5
«134

Comments

  • RaymanRayman Posts: 14,652
    edited 2012-08-02 15:42
    Here's a partial screen shot, so you can see what it looks like:
    FontCaptureScreen.png
    533 x 485 - 57K
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-02 16:56
    This looks interesting. What program are you writing that in?

    I've looked at a number of font solutions. I've ended up using a free program to create a bitmap of all the fonts, then a custom vb.net program to turn that into a file that can be loaded rapidly off an sd card. It is a solution that works for any font and means you can use any font editor out there to build a new .fon file, then convert it when it is done.

    Hmm - that is a bit confusing to explain. At the end of the day, it is about a 30second process to create a font file.

    However I've never been entirely happy with it being in vb.net as that is not a program that can be run on linux/mac etc. Maybe we can share some code here?
  • RaymanRayman Posts: 14,652
    edited 2012-08-03 03:34
    Dr_A, there are lots of font tools out there, but I wasn't completely happy with any I found...
    This is written in MFC, so there's no way it will work on Linux (well, at least not without VirtualBox).
    If you tell me about your .fon format, I can try to include that for you though.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-03 05:59
    Hi Rayman,

    I have been using this free program to convert true type fonts into bitmaps http://www.angelcode.com/products/bmfont/

    That generates two things, a bitmap picture, and a text file that tells you where each character is in the picture. That in itself could be decoded by a spin program. However, I process that a bit more and combine them into one file which has a jump table at the beginning and also changes the foreground and background color to whatever you want. That means that you can get the pixel data for a character quickly - take the ascii value which points to the jump location in the header and the width and height and number of pixels, jump to that location, and then read off the pixels. This preprocessing makes the retrieval fast. This is the spin code to print a character:
    PUB TextChar(FileN,ascii,x,y) | jump,size,width,height,ramaddress,xoffset,yoffset,xadvance   ' read out a character from a .ifn file stored in ram
      ' pass Fonttable = global
      ' moves to next line if off the end
        ramaddress := RamLocation[FileN]
        jump := ReadRamLong(ramaddress,(ascii << 2) + 256)   ' jump location = ascii *4 plus fonttable + 256
        SpinRamToHub(@buffer2,ramaddress+jump>>1,20)                ' read in the width height as a block = quicker than readramlong
        size := long[@buffer2][0]                           ' size in pixels of this character
        xadvance := long[@buffer2][5]                       ' amount to move to next character   
        'if size > 0 and ascii <> 32                         ' no need to print anything if size is zero or read more font data
        width := long[@buffer2][1]                        ' width
        height := long[@buffer2][2]                       ' height
        xoffset := long[@buffer2][3]                      ' xoffset to move
        yoffset := long[@buffer2][4]                      ' yoffset to move
        if (x+width -1 + xoffset) > screenwidth
            crlf                                            ' do a new line if it won't fit
            x := curx
            y := cury
        if ascii < 32
          xadvance := 0                                     ' add nothing if a non displaying character
        if ascii == 13                                  ' carriage return
            cr
            x := curx
        if ascii ==10                                   ' line feed (new line)
            lf
            y := cury
        if ascii > 32 ' space not displayed
          Draw(x+xoffset,y+yoffset,x+width-1+xoffset,y+height-1+yoffset)      ' draw on screen
          RamToDisplay(ramaddress+((jump+32)>>1),size)       ' move bytes from ram out to the display
        return x + xadvance 
    

    If this is helpful I can give you the vb.net code to produce these font files.

    As for editing fonts, hmm, that is a separate issue. Truetype or bitmap fonts?
  • RaymanRayman Posts: 14,652
    edited 2012-08-03 08:02
    Ok, I think this may be all I need... Can you post an example .fon file here, so I can be sure I get it right?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-04 04:27
    Hi Rayman,

    Sorry about the delay. The angelcode program takes a .ttf font file and produces two files - a text file and a png graphic. I save the text files with a .fnt extension.

    The png file is white on black and you can use the grayscale to convert from any foreground and any background.

    I'll post the full vb.net program below, but this is the routine to load up the .png and .fnt files and draw the png on a picturebox
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            Dim Filepath, Sourcefile, DestinationFile, pngfile As String
            Filepath = TextBox2.Text ' directory
            OpenFileDialog1.Multiselect = False
            OpenFileDialog1.InitialDirectory = Filepath
            OpenFileDialog1.FileName = "*.fnt" ' select .fnt file. 
            OpenFileDialog1.ShowDialog() ' open the openfile dialog
            Sourcefile = OpenFileDialog1.FileName ' get the filename
            Sourcefile = Strings.Mid(Sourcefile, Strings.Len(Filepath) + 2) ' strip off directory
            pngfile = Strings.Left(Sourcefile, Strings.Len(Sourcefile) - 4) + "_0.png"
            Dim img As Image
            Dim fs As New FileStream(Filepath + "\" + pngfile, IO.FileMode.Open) ' open the picture
            img = Image.FromStream(fs) ' load in the file
            fs.Close() ' close the picture
            PictureBox3.Image = img ' display the picture
            RichTextBox1.LoadFile(Filepath + "\" + Sourcefile, RichTextBoxStreamType.PlainText)
            TextBox3.Text = Sourcefile
            TextBox4.Text = Strings.Left(Sourcefile, Strings.Len(Sourcefile) - 4) + ".ifn"
        End Sub
    

    Then you can use a color selection box to change the colors (eg white on black to black on white).

    This is the code that does the conversion
        Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
            Dim FontArray(262144) As Byte ' won't need all of this!
            Dim Ascii, x, y, i, j, Width, Height, Xoffset, Yoffset, Xadvance, Fontvalue As Long
            Dim RedDiff, GreenDiff, BlueDiff, Backred, BackGreen, BackBlue, ForeRed, ForeGreen, ForeBlue As Single
            Dim ILIred, ILIgreen, ILIblue As Single
            Dim ILIredbyte, ILIgreenbyte, ILIbluebyte As Long
            Dim ILIlong As Long
            Dim ILIhigh, ILIlow As Byte
            Dim Mycolor As Color
            Dim MyBitmap As New System.Drawing.Bitmap(PictureBox3.Image) 'image from picture box
            Dim LineOfText As String
            Dim Counter, StartCharacter As Long
            Dim Header As String
            Dim FontHeight As Byte
            Dim start1, start2, finish1, finish2, loopstep1, loopstep2 As Long
            Backred = PictureBox5.BackColor.R
            BackGreen = PictureBox5.BackColor.G
            BackBlue = PictureBox5.BackColor.B
            ForeRed = PictureBox4.BackColor.R
            ForeGreen = PictureBox4.BackColor.G
            ForeBlue = PictureBox4.BackColor.B
            RedDiff = Backred - ForeRed
            GreenDiff = BackGreen - ForeGreen
            BlueDiff = BackBlue - ForeBlue
            Counter = 256 + 1024 ' first 256 bytes are data about the font - size, bold, back and foreground colors etc and the next 1024 are the jump tables
            StartCharacter = Counter
            Header = "Portrait  "
            Header += "Forecolor" + Strings.Chr(ForeRed) + Strings.Chr(ForeGreen) + Strings.Chr(ForeBlue)
            Header += "Backcolor" + Strings.Chr(Backred) + Strings.Chr(BackGreen) + Strings.Chr(BackBlue)
            For Each Line As String In RichTextBox1.Lines ' much faster than for i=0 to richtextbox1.lines.length
                LineOfText = Line
                If Strings.Left(LineOfText, 4) = "info" Then
                    Header += LineOfText ' add this line to header string
                End If
                If Strings.Left(LineOfText, 6) = "common" Then
                    FontHeight = Strings.Val(Strings.Mid(LineOfText, 19, 2))
                End If
                If Strings.Left(LineOfText, 7) = "char id" Then
                    'char id=32   x=96    y=21    width=1     height=1     xoffset=0     yoffset=23    xadvance=7     page=0  chnl=15
                    Ascii = Strings.Val(Strings.Mid(LineOfText, 9, 3)) ' sub 17 
                    x = Strings.Val(Strings.Mid(LineOfText, 16, 3))
                    y = Strings.Val(Strings.Mid(LineOfText, 24, 3))
                    Width = Strings.Val(Strings.Mid(LineOfText, 36, 3))
                    Height = Strings.Val(Strings.Mid(LineOfText, 49, 3))
                    Xoffset = Strings.Val(Strings.Mid(LineOfText, 63, 3))
                    Yoffset = Strings.Val(Strings.Mid(LineOfText, 77, 3))
                    Xadvance = Strings.Val(Strings.Mid(LineOfText, 92, 3))
                    StartCharacter = Counter ' start of the data about this character
                    LongtoArray(FontArray, (Ascii * 4 + 256), StartCharacter) ' 4 byte jump table
                    LongtoArray(FontArray, StartCharacter, Width * Height) ' size of the character
                    LongtoArray(FontArray, StartCharacter + 4, Width) ' width
                    LongtoArray(FontArray, StartCharacter + 8, Height) ' height
                    LongtoArray(FontArray, StartCharacter + 12, Xoffset) ' xoffset
                    LongtoArray(FontArray, StartCharacter + 16, Yoffset) ' yoffset
                    LongtoArray(FontArray, StartCharacter + 20, Xadvance) 'xadvance
    
                    Counter += 32 ' start of the pixels - add 32
                    start1 = y ' outside loop
                    start2 = x ' inside loop
                    finish1 = y + Height - 1
                    finish2 = x + Width - 1
                    loopstep1 = 1 ' add 1 to each step
                    loopstep2 = 1
                    For j = start1 To finish1 Step loopstep1
                        For i = start2 To finish2 Step loopstep2
                            'If RadioButton1.Checked = True Then
                            Mycolor = MyBitmap.GetPixel(i, j) ' get the color
                            'Else
                            'Mycolor = MyBitmap.GetPixel(j, i) ' swap i and j if in landscape mode
                            'End If
                            Fontvalue = Mycolor.R ' grayscale so RGB should be the same
                            ILIred = Backred - (RedDiff * Fontvalue) / 255
                            ILIgreen = BackGreen - (GreenDiff * Fontvalue) / 255
                            ILIblue = BackBlue - (BlueDiff * Fontvalue) / 255
                            If ILIred > 255 Then ILIred = 255 ' limit testing
                            If ILIred < 0 Then ILIred = 0
                            If ILIgreen > 255 Then ILIgreen = 255
                            If ILIgreen < 0 Then ILIgreen = 0
                            If ILIblue > 255 Then ILIblue = 255
                            If ILIblue < 0 Then ILIblue = 0
                            ILIredbyte = CByte(ILIred)
                            ILIgreenbyte = CByte(ILIgreen)
                            ILIbluebyte = CByte(ILIblue)
                            ILIredbyte = ILIredbyte >> 3 ' round down 3 places
                            ILIgreenbyte = ILIgreenbyte >> 2  ' round down two places
                            ILIbluebyte = ILIbluebyte >> 3 ' round down 3 places
                            ILIlong = (ILIredbyte << 11) + (ILIgreenbyte << 5) + ILIbluebyte
                            ILIhigh = HighByte(ILIlong) ' get high and low bytes
                            ILIlow = LowByte(ILIlong)
                            FontArray(Counter) = ILIlow ' store the pixels
                            FontArray(Counter + 1) = ILIhigh
                            Counter += 2
                        Next
                    Next
                    Counter += 4 ' so last pixel included ** this doesn't work, not sure why, file looks ok in hexedit **
                    Counter = Counter And &HFFFFFFFC ' mask off lower 2 bits, same as rounding to the nearest long so data is long aligned
                End If
            Next
            ' store the size of the array at the beginning of the array
            ' because it is much easier to read in blocks of 256 bytes, round up
            Counter = Counter + 256 ' round up by one block
            Counter = Counter And &HFFFFFF00 ' mask off lower 256 - same as rounding up
            LongtoArray(FontArray, 0, Counter) ' add counter to array at location 0 (file length)
            ' put in the header string
            For i = 1 To Strings.Len(Header)
                FontArray(i + 3) = Strings.Asc(Strings.Mid(Header, i, 1))
            Next
            ' Store a byte for the height in a fixed location - easier to decode
            For i = 1 To 6
                FontArray(i + 244) = Strings.Asc(Strings.Mid("Height", i, 1)) ' so can see the start of the jump table
            Next
            FontArray(251) = FontHeight
            ' store some text
            For i = 1 To 4
                FontArray(i + 251) = Strings.Asc(Strings.Mid("Jump", i, 1)) ' so can see the start of the jump table
            Next
            Dim DestinationFile As String
            DestinationFile = TextBox2.Text + "\" + TextBox4.Text
            Dim OutputILI As New FileStream(DestinationFile, FileMode.Create, FileAccess.Write)
            OutputILI.Write(FontArray, 0, Counter) ' save bytes
            OutputILI.Close()
            MsgBox("Finished")
        End Sub
    

    Attached are the vb.net program (in ILI9325.zip) and three font files, the .png, .fnt and the output .ifn file. The .ifn one is the one that spin code in the previous post can decode.

    The font is stored as one word per pixel suitable for touchscreens ie RRRRRGGG_GGGBBBBB

    The catch here is that when you store fonts as raw pixels most fonts end up 30-60k in size, and if you want to read characters quickly then you need external ram rather than reading each one off the sd card. I think you have some external ram working, is that correct?
  • RaymanRayman Posts: 14,652
    edited 2012-08-04 06:49
    Dr_A, you have me a little confused now... Is there a .fon file that is the final output you want? Are you looking for 16 bpp or 1 bpp?
    Reading your last post, I think you want 16 bpp, but that winds up bigger than what you can hold in the Propeller...

    My font editor works in 24 bpp, so it can generate the 16 bpp you want.
    But, personally, I think 1 bpp is the best way to go for LCD screens, at least initially.
    Small size fonts really don't get anti-aliased anyway, so you don't really gain anything in 16 bpp, except for the ClearType effect.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-04 17:30
    Hi Rayman, yes I came to this code in a rather roundabout way. Everything is ultimately determined by the way the touchscreens work. On the TV and VGA drivers you can have one pixel per bit and effectively write 32 pixels with one assembly instruction. With the touchscreen it needs a set of instructions to give the x,y location and the width and height. Then dump out w*h pixels as a block. It ends up much faster to create characters as individual bitmaps rather than sending out pixels one at a time. So in the end there is no time cost or overhead in sending out a fully aliased font compared with sending out an unaliased one. See below for a screenshot - this demonstrates one advantage of creating fonts externally using aliasing and variable foreground and background colors. For the calculator screen, that is a particular green background. Sample the RGB values and use those as the background for a font and you can print characters on that screen. The disadvantage of course is that the entire font file is set up for black, green and aliased shades of black to green, so if you want black on white you need to create another font file.

    I do actually have some code to send out the propeller font using the more traditional method and I use that for bootup messages, in particular messages related to starting up the SD card because if the SD card is not working you can't display a font off the SD card. The catch is that it is slower.
    PUB Propfont_out(ascii) | address,pixels
        Draw(curx,cury,curx+15,cury+31)                     ' location to start drawing 
        address := $8000 + (ascii >> 1) << 7                ' get rom address
        repeat 32                                           ' 32 rows per character, split in two parts
          pixels := long[address]                           ' get rom font data
          pixels := pixels >> (ascii & 1)                   ' shift for odd characters
          repeat 16 ' 16 columns
            if pixels & 1
              Pixel(%00000111_11100000)                     ' foreground color RRRRRGGG_GGGBBBBB
            else
              Pixel(%00000000_00000000)                     ' background color 
            pixels := pixels >> 2                           ' alternate pixels interleaved so shift 2
          address += 4 
        curx +=16
        if curx >239                                        ' new line
          Propfontcrlf
    

    One thing that could be possible is to take that code and turn it into pasm and then it should be a lot faster and fonts will be a lot smaller.

    The angelcode font program has a checkbox for outputting aliased or non aliased fonts so we could think about non aliased fonts with 32 pixels per long and which will be 1/16th of the size compared with aliased bitmap ones. An advantage is that you could change the foreground and background colors on the fly. A disadvantage will be that the code for variable width fonts will be more complex.

    Your thoughts?
    1017 x 912 - 181K
    calc.jpg 181.1K
  • jmgjmg Posts: 15,173
    edited 2012-08-08 17:59
    I've used this
    http://www.mikroe.com/eng/products/view/683/glcd-font-creator/

    which works with a .LCD file, in ascii format, one example, snipped for size.
    Looks to store 24bpp, but as just one of two values for a Monochrome Font.

    This would be a good file format to support ?
    53='5', and I think this stores left-column first (ie Top-Left, DecY ,DecX order) 0=ON ?
    <?xml version="1.0" encoding="iso-8859-1"?>
    <FONT>
         <FONTNAME>TerminalB9</FONTNAME>
         <FONTSIZE WIDTH="6" HEIGHT="12" PROPORTIONAL="0" FONTKIND="0"/>
         <RANGE FROM="32" TO="127"/>
         <CHARS>
              <CHAR CODE="32" PIXELS="16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215"/>
    .. removed chars..
               <CHAR CODE="53" PIXELS="16777215,0,0,0,0,0,16777215,16777215,0,16777215,16777215,16777215,16777215,0,0,0,0,0,16777215,16777215,0,0,16777215,16777215,16777215,0,16777215,16777215,16777215,0,16777215,16777215,16777215,0,16777215,16777215,16777215,0,16777215,16777215,16777215,0,0,0,0,0,16777215,16777215,16777215,0,16777215,16777215,16777215,16777215,0,0,0,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215,16777215"/>
    
         </CHARS>
    </FONT>
    
  • RaymanRayman Posts: 14,652
    edited 2012-08-08 18:07
    Dr_A, for <3" screens, I think non-antialiased is better. For bigger screens, I'm not 100% sure yet.
    I think I have decided that the ROM font works well on this 5" Newhaven LCD, so it may be moot there.
    Still, for large text, and for reading, the cleartype effect helps a lot. I think I can do it with 4 bpp though...

    jmg, that program looks a lot like what I'm trying to do... Maybe I wouldn't have done this if I'd found that one... Still, I think mine is unique in capturing the Parallax font with it's code page (I'm hoping to make an on-Prop spin editor, so I'll want that...) and also outputting to VG_HiRes_Text . That format looks fine.
  • jmgjmg Posts: 15,173
    edited 2012-08-08 21:39
    On the topic of fonts, I also found this

    http://terminus-font.sourceforge.net/

    The sizes present are 6x12, 8x14, 8x16, 10x18, 10x20, 11x22, 12x24, 14x28 and 16x32.
    The styles are normal and bold (except for 6x12), plus EGA/VGA-bold for 8x14 and 8x16.


    I see your Font Editor imports in Pixels, or Point, whilst the glcd-font-creator I linked above, imports in Point only, and so getting the wanted Pixel XY is more of a lottery.

    glcd-font-creator does have a Export Range feature, but it annoyingly lacks a Raster-Order choice (and defaults to a dY scan).

    The ideal is to
    * Export each Char with the Char as a comment, (allows users to cut/paste/merge)
    * Give users the choice of Mirror X, Mirror Y and dX or dY raster scan exports

    Most assemblers I've used support DB and DW tables, but I cannot see that mentioned in the Prop Assembler reference, it would be a useful option.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-09 01:34
    One nice thing about that angelcode program is you can process any font that happens to be on your computer. I agree that aliasing small fonts doesn't matter so much. In which case you could easily modify that vb.net program to pack bits into longs in any format you like. 32 pixels per long if you wanted. And you can create any size font and any font format you want. That means you can do your editing on a .ttf font editor and there are many of those around.

    Maybe we can think about a format for fonts for the propeller?
  • jmgjmg Posts: 15,173
    edited 2012-08-09 02:17
    Dr_Acula wrote: »
    Maybe we can think about a format for fonts for the propeller?

    Good idea, see the example in #10.

    That is ASCII, so is easy to parse, is text editor friendly, and it has the support of a free viewer.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-09 02:53
    That looks interesting. I agree ascii is easier to read. Is there an example for proportional width fonts - ie a width value for each character?
  • jmgjmg Posts: 15,173
    edited 2012-08-09 03:03
    Dr_Acula wrote: »
    That looks interesting. I agree ascii is easier to read. Is there an example for proportional width fonts - ie a width value for each character?

    hehe, I think that is targeting Fixed-width LCDs, but there is nothing preventing allowing an optional field of
    WIDTH="6" HEIGHT="12"
    
    on a per-character basis (usually only one would vary, in the plot direction, if you expected both to vary, then the issue of handling offsets occurs.

    You would need to scan the file and build a separate index table if you wanted to remove the dead-space, and the Free Viewer would likely break.....

    On some instances, simpler indexing might be more important than smallest size (eg Font in QuadSPI ), but for RAM hosted fonts, smallest overall footprint would be the target.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-09 06:46
    Good points there. I'm probably using variable width fonts more than fixed width ones. This is Times Roman and I like Arial as well. And even though displays can do all colors (eg white on blue, green on black), for text I still like black on white.

    I do like the idea of an ascii readable font. For speed of processing, would you make the text for something like "width" always a fixed number of characters? That might make the conversion quicker, eg if you had leading zeros then the position of the text describing the width is always in a fixed location so you can do a simple "ascii to value" conversion.

    I built a jump table at the front of the font as it makes it quicker to send out the pixels. Still, it takes about one and a half seconds to dump out all the display in this photo. I have a vague feeling that if you gave up aliasing and just went for white and black, plus a pasm driver, plus smaller font files, this could be dumped to the display quicker.
    800 x 600 - 74K
  • jmgjmg Posts: 15,173
    edited 2012-08-09 13:49
    Dr_Acula wrote: »
    For speed of processing, would you make the text for something like "width" always a fixed number of characters? That might make the conversion quicker, eg if you had leading zeros then the position of the text describing the width is always in a fixed location so you can do a simple "ascii to value" conversion.

    I'm not completely following the question, but assuming too much about the format can be dangerous, as it may be Auto-created.

    Taking the example above, of
    WIDTH="6"
    Usually you would 'key' off 'WIDTH=', or perhaps even 'WIDTH="', and then trim using the next ", which should leave a number string, which could be in any base.

    The time needed to work on ASCII files is not important, as you usually finally create a Table, which is then assembled/compiled.

    eg the glcd-font-creator above is somewhat sluggish in load/trim/export, but 10 seconds is not a big problem.
  • RaymanRayman Posts: 14,652
    edited 2012-08-09 14:00
    For binary export options, I'm thinking of 3 basic settings:
    Byte alignment (none, 1, 2, 4)
    Scan direction (8 different ways)
    Bits per pixel (1,4,8,24)

    This is for fixed-width. For variable width, I was thinking about having all characters still taking the same # of bytes to define plus 1 byte to say the width. That way, you can quickly index the characters... But, this takes up more space than necessary, so I'm still mulling it over...
  • jmgjmg Posts: 15,173
    edited 2012-08-09 14:01
    Rayman wrote: »
    jmg, that program looks a lot like what I'm trying to do... Maybe I wouldn't have done this if I'd found that one...

    If you are looking for suggestions for your font editor, these are the 'peeves' I found with glcd-font-creator

    * It lacks raster order export choices, (seems to always TopLeft, dY then dX) - needs Mirror X, Mirror Y, Scan X.Y and Y.X choices.

    * It can insert/delete rows, but ONLY on the edges, so (eg) making a taller variant is more work than it should be.
    Insert row/column choices of Black/White/Clone Present row would almost eliminate pixel-clicking.

    * It lacks a screen-preview mode, your's is way ahead there. Allows a screen capture font thumbnail record.

    Pluses :
    It has a simple ASCII file, so a user can always add external code to fix some of this.
  • jmgjmg Posts: 15,173
    edited 2012-08-09 14:16
    Our postings crossed...
    Rayman wrote: »
    For binary export options, I'm thinking of 3 basic settings:
    Byte alignment (none, 1, 2, 4)
    Scan direction (8 different ways)
    Bits per pixel (1,4,8,24)

    Sounds great. When you say 'binary' do you mean ASM type source ?
    There, I would add each Char value as a comment, and allow DB 12H,34H,... and DW 1234H,5678H, ... syntax.
    I've struck some assemblers that choke above some line-length, so an ASM line length option would fix that.
    ie if practical I prefer one line per char, but larger fonts, and Colour, will give long lines.


    Rayman wrote:
    This is for fixed-width. For variable width, I was thinking about having all characters still taking the same # of bytes to define plus 1 byte to say the width. That way, you can quickly index the characters... But, this takes up more space than necessary, so I'm still mulling it over...

    Perhaps an option ? As you say, Fixed is simpler and Serial flash is dirt cheap, but someone may want to target RAM use, and RAM is precious indeed on a Prop.

    The most compact would be a separate byte-sized width table, and you simply run through that, adding the widths to find your packed Char index. Code to run-sum should be << the alternative N.Words index array.

    Or maybe all 3 : Simple/Compact/Fast, but that may be over-doing a font editor... ?
  • RaymanRayman Posts: 14,652
    edited 2012-08-09 15:18
    by binary, what I meant is a raw binary output...
    One that you can include in Prop Tool like this:
    DAT 'bitmap font data
    font long
            file "ProgoCo.dat"
    

    I do like that XML output format, BTW. I also like the idea of defining a range of characters to output.
    In some cases, you might only want to define the "normal" characters...
  • jmgjmg Posts: 15,173
    edited 2012-08-09 16:31
    Rayman wrote: »
    by binary, what I meant is a raw binary output...
    One that you can include in Prop Tool like this:


    So that is an ASM source file ? - like you export now as :

    long  $00000000,$00000000,$00000000,$00000000,$00000000,$00000000,$00000000,$00000000
    long  $ffffffff,$00000000,$ffffffff,$00000000,$00000000,$00000000,$00000000,$00000000
    etc
    

    So I would suggest also doing what we do here, which is Asm DB, with ASM comments like :
    ; ReverseX=Y ; ReverseY=N; Width=8; Height=16
    ;xx12345678 ASCII No / Index :    1
    ;1  ###### 
    ;2 #      #
    ;3 # #  # #
    ;4 #      #
    ;5 # #### #
    ;6 #  ##  #
    ;7 #      #
    ;8  ###### 
     DB 07EH,081H,099H,0BDH,081H,0A5H,081H,07EH
    
    The AsciiArt is what you expect to see, the DB may be bit-flipped
    ( with perhaps DW option, but then an endian choice may be needed...)
    This would allow using almost any assembler to create large hex files for Font ROMs
    Rayman wrote:
    I do like that XML output format, BTW. I also like the idea of defining a range of characters to output.
    In some cases, you might only want to define the "normal" characters...

    Yes, and in other cases, you may wish to merge fonts from multiple archives/sources...
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-09 17:11
    Great ideas there. Re the numbers if you had
    WIDTH=6
    HEIGHT=12

    then if you have a bigger font
    WIDTH=15
    HEIGHT=12

    the position of "H" changes and then you have to search for a string which takes more time. I was thinking you write
    WIDTH=06
    HEIGHT=12

    and then H is always in the same place in the file.

    Another idea - instead of writing in longs, what about writing in binary. Assume all fonts will be less than 32 wide (probably true). Ok it wastes a bit of memory for small fonts but it very much simplifies both editing and processing. eg an A
    WIDTH=05
    HEIGHT=06
    long %00100000_00000000_00000000_00000000
    long %01010000_00000000_00000000_00000000
    long %10001000_00000000_00000000_00000000
    long %11111000_00000000_00000000_00000000
    long %10001000_00000000_00000000_00000000
    long %10001000_00000000_00000000_00000000
    
  • RaymanRayman Posts: 14,652
    edited 2012-08-09 17:33
    the above two examples are what I'd call "text" output... That is, each byte in the output is an ascii character...

    binary output means that for, e.g., this 8x16 x 1bpp font, the first 16 bytes in the file represent the 1st character...
  • jmgjmg Posts: 15,173
    edited 2012-08-09 18:42
    Rayman wrote: »
    the above two examples are what I'd call "text" output... That is, each byte in the output is an ascii character...

    binary output means that for, e.g., this 8x16 x 1bpp font, the first 16 bytes in the file represent the 1st character...

    but why work in hard-to--check binary, given you import into a compiler, or assembler, anyway, to create the full run-time ?
    ( or assemble multiple files to a HEX file for loading a Font-rom).
  • jmgjmg Posts: 15,173
    edited 2012-08-09 18:48
    Dr_Acula wrote: »
    the position of "H" changes and then you have to search for a string which takes more time. I was thinking you write

    I'm not following the issue here ?
    On a PC, who cares how many microseconds a string search adds ?

    On the target, you would not work with full ASCII-parser record files, you would use equates, or DB/long arrays, and even there
    the source-file size is a total 'don't care' these days, so I create for clarity.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-09 18:49
    Would you need to worry about jump tables? Consider a font 8x5, and maybe using 8 longs per character. So "A" is at location 65x8. That is fine but it does involve a multiply. And the next font might be 9 pixels high. I put a lookup table at the beginning of the font file because it saved that multiply but maybe it isn't worth worrying about. Speed vs file size I guess.

    And for more efficiency regarding file size, maybe have two file types - one for "bytes" and one for "longs", ie ones that are 1-8 pixels wide and ones that are more than 8 pixels wide? Then you could display both in a way that you can edit easily as you can see the pixels.
    On the target, you would not work with full ASCII-parser record files, you would use equates, or DB/long arrays, and even there
    the source-file size is a total 'don't care' these days, so I create for clarity.

    ah - miscommunication there, I was thinking you wanted an ascii file on the target.

    Many possibilities here. I wonder if one solution is to produce an ascii file that is actually code you can paste into the DAT section of code?

    he he - how about a program you run on the PC that produces a spin/pasm program that contains not only the font data in a DAT section but also the code needed to process it? Then if the format of the file changes the code changes too. Crazy stuff. Quite doable of course.
  • jmgjmg Posts: 15,173
    edited 2012-08-09 19:01
    Dr_Acula wrote: »
    Would you need to worry about jump tables? Consider a font 8x5, and maybe using 8 longs per character. So "A" is at location 65x8. That is fine but it does involve a multiply. And the next font might be 9 pixels high. I put a lookup table at the beginning of the font file because it saved that multiply but maybe it isn't worth worrying about. Speed vs file size I guess.

    I see three levels of choice :
    Simplest : No tables, all chars are identical sizes and no 'gaps'. Suits serial FLASH fonts.
    Compact : For variable width fonts, you need a width, but an Index table is not mandatory, you can quick-sum the width bytes.
    Fastest: : Uses a Index table, and could pack Width into the Table-change, which needs a EOF entry.
    This is larger, as the font index will be at least 16 bits, and could be 24 or 32 bits per character.

    An index table has one other advantage, in that it allows sparse fonts - you can skip unused values, but still use an accepted/portable ASCII value index to the ones you do have. - eg A meter might support 0..9, and A,V,Hz (etc)
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-09 19:52
    I like it! Simple, Compact, Fast. You could create code for each.

    I'm going to have a go at writing a "True Type Font to Spin converter" when I get home. I've got this idea that the driver and the data get created at the same time. Later, if one lost the program to create the code, you could still edit a font as the code is human readable. And you could even automatically generate comments - eg your Simplest one is going to be fixed height and width so all the data is just one character after another. But if you had a variable width font, you could put in the dat section the width of the letter as a hex byte, but more importantly, you could put a comment in next to that to say what it is. So again, later you could edit this despite not having access to the code that made the program. Ditto you could edit the Spin - translate it to pasm if you wanted.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-10 02:27
    Very preliminary experiments
    DAT
      FontBitmap
    ' character  32  
    Byte  8 ' xadvance
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    ' character  33 !
    Byte  8 ' xadvance
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00001000
    Byte %00001000
    Byte %00001000
    Byte %00001000
    Byte %00001000
    Byte %00000000
    Byte %00000000
    Byte %00001000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    ' character  34 "
    Byte  8 ' xadvance
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00110110
    Byte %00110110
    Byte %00100100
    Byte %00100100
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    ' character  35 #
    Byte  8 ' xadvance
    Byte %00000000
    Byte %00000000
    Byte %00000000
    Byte %00010010
    Byte %00010010
    Byte %00100100
    Byte %01111110
    Byte %00100100
    Byte %00100100
    Byte %01111110
    Byte %00100100
    Byte %01001000
    Byte %01001000
    Byte %00000000
    Byte %00000000
    Byte %00000000
    

    produced by this
        Private Sub Button7_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button7.Click
            Dim SpinProgram(10000) As String
            Dim Ascii, x, y, i, j, Width, Height, Xoffset, Yoffset, Xadvance, Fontvalue, Base As Long
            Dim Mycolor As Color
            Dim MyBitmap As New System.Drawing.Bitmap(PictureBox3.Image) 'image from picture box
            Dim LineOfText As String
            Dim FontHeight As Byte
            Dim LineCounter As Long
            Dim start1, start2, finish1, finish2, loopstep1, loopstep2 As Long
            Dim FontString, Destinationfile As String
    
    
    
            LineCounter = 30 ' DAT section
            SpinProgram(LineCounter) = "DAT"
            LineCounter += 1
            SpinProgram(LineCounter) = "  FontBitmap"
            LineCounter += 1
            For Each Line As String In RichTextBox1.Lines ' much faster than for i=0 to richtextbox1.lines.length
                LineOfText = Line
                If Strings.Left(LineOfText, 6) = "common" Then
                    FontHeight = Strings.Val(Strings.Mid(LineOfText, 19, 2))
                    Base = Strings.Val(Strings.Mid(LineOfText, 27, 2)) ' base is a better measure of height
                End If
                If Strings.Left(LineOfText, 7) = "char id" Then
                    'char id=32   x=96    y=21    width=1     height=1     xoffset=0     yoffset=23    xadvance=7     page=0  chnl=15
                    Ascii = Strings.Val(Strings.Mid(LineOfText, 9, 3)) ' sub 17 
                    x = Strings.Val(Strings.Mid(LineOfText, 16, 3))
                    y = Strings.Val(Strings.Mid(LineOfText, 24, 3))
                    Width = Strings.Val(Strings.Mid(LineOfText, 36, 3))
                    Height = Strings.Val(Strings.Mid(LineOfText, 49, 3))
                    Xoffset = Strings.Val(Strings.Mid(LineOfText, 63, 3))
                    Yoffset = Strings.Val(Strings.Mid(LineOfText, 77, 3))
                    Xadvance = Strings.Val(Strings.Mid(LineOfText, 92, 3))
                    start1 = y ' outside loop
                    start2 = x ' inside loop
                    finish1 = y + Height - 1
                    finish2 = x + Width - 1
                    loopstep1 = 1 ' add 1 to each step
                    loopstep2 = 1
                    SpinProgram(LineCounter) = "' character " + Strings.Str(Ascii) + " " + Strings.Chr(Ascii)
                    LineCounter += 1
                    SpinProgram(LineCounter) = "Byte " + Strings.Str(Xadvance) + " ' xadvance"
                    LineCounter += 1
                    ' add in blank lines at top
                    For i = 1 To Yoffset
                        SpinProgram(LineCounter) = "Byte %00000000"
                        LineCounter += 1
                    Next
                    For j = start1 To finish1 Step loopstep1
                        FontString = "" ' now add in the font data
                        For i = 1 To Xoffset
                            FontString += "0" ' add leading spaces
                        Next
                        For i = start2 To finish2 Step loopstep2
                            Mycolor = MyBitmap.GetPixel(i, j) ' get the color
                            Fontvalue = Mycolor.R ' grayscale so RGB should be the same
                            If Fontvalue > 128 Then
                                FontString += "1"
                            Else
                                FontString += "0"
                            End If
                        Next
                        FontString = Strings.Left(FontString + "00000000", 8) ' pad so always 8 wide
                        FontString = "Byte %" + FontString
                        SpinProgram(LineCounter) = FontString
                        LineCounter += 1
                    Next
                    ' add in lines at the bottom
                    For i = 1 To FontHeight - (Height + Yoffset)
                        SpinProgram(LineCounter) = "Byte %00000000"
                        LineCounter += 1
                    Next
                End If
            Next
            Destinationfile = TextBox2.Text + "\" + TextBox5.Text
            FileOpen(1, Destinationfile, OpenMode.Output)
            For i = 0 To LineCounter
                Print(1, SpinProgram(i) + vbCrLf) ' send to disk
            Next
            FileClose(1)
    
Sign In or Register to comment.